aria-code 4.1.3__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 (284) hide show
  1. agents/__init__.py +32 -0
  2. agents/base.py +190 -0
  3. agents/deep/__init__.py +37 -0
  4. agents/deep/calibration_loop.py +144 -0
  5. agents/deep/critic.py +125 -0
  6. agents/deep/deepen.py +193 -0
  7. agents/deep/models.py +149 -0
  8. agents/deep/pipeline.py +164 -0
  9. agents/deep/quant_fusion.py +192 -0
  10. agents/deep/themes.py +95 -0
  11. agents/deep/tiers.py +106 -0
  12. agents/financial/__init__.py +10 -0
  13. agents/financial/catalyst.py +279 -0
  14. agents/financial/debate.py +145 -0
  15. agents/financial/earnings.py +303 -0
  16. agents/financial/fundamental.py +159 -0
  17. agents/financial/macro.py +99 -0
  18. agents/financial/news.py +207 -0
  19. agents/financial/risk.py +132 -0
  20. agents/financial/sector.py +279 -0
  21. agents/financial/synthesis.py +274 -0
  22. agents/financial/technical.py +258 -0
  23. agents/portfolio_agent.py +333 -0
  24. agents/realty/__init__.py +62 -0
  25. agents/realty/asset_diagnosis.py +150 -0
  26. agents/realty/business_match.py +165 -0
  27. agents/realty/cashflow_verify.py +208 -0
  28. agents/realty/contract_rules.py +209 -0
  29. agents/realty/energy_anomaly.py +188 -0
  30. agents/realty/exit_settlement.py +207 -0
  31. agents/realty/fulfillment_risk.py +205 -0
  32. agents/realty/ops_optimize.py +159 -0
  33. agents/realty/revenue_share.py +214 -0
  34. agents/registry.py +144 -0
  35. agents/sports/__init__.py +0 -0
  36. agents/sports/football_agent.py +169 -0
  37. agents/team.py +289 -0
  38. aliyun_data_client.py +660 -0
  39. apps/README.md +12 -0
  40. apps/__init__.py +2 -0
  41. apps/channels/README.md +15 -0
  42. apps/cli/README.md +13 -0
  43. apps/cli/__init__.py +2 -0
  44. apps/cli/bootstrap.py +99 -0
  45. apps/cli/codegen_paths.py +29 -0
  46. apps/cli/commands/__init__.py +16 -0
  47. apps/cli/commands/analysis_cmds.py +288 -0
  48. apps/cli/commands/backtest_cmds.py +1887 -0
  49. apps/cli/commands/broker_cmds.py +1154 -0
  50. apps/cli/commands/business_workflow_cmds.py +289 -0
  51. apps/cli/commands/catalog.py +84 -0
  52. apps/cli/commands/data_cmds.py +405 -0
  53. apps/cli/commands/diagnostic_cmds.py +179 -0
  54. apps/cli/commands/diagnostic_ops_cmds.py +696 -0
  55. apps/cli/commands/finance_render.py +12 -0
  56. apps/cli/commands/market.py +399 -0
  57. apps/cli/commands/market_cmds.py +1276 -0
  58. apps/cli/commands/market_context.py +425 -0
  59. apps/cli/commands/market_render.py +7 -0
  60. apps/cli/commands/model_cmds.py +1579 -0
  61. apps/cli/commands/ops_cmds.py +668 -0
  62. apps/cli/commands/portfolio_cmds.py +962 -0
  63. apps/cli/commands/report.py +377 -0
  64. apps/cli/commands/scaffold_templates.py +617 -0
  65. apps/cli/commands/session_cmds.py +179 -0
  66. apps/cli/commands/session_ux_cmds.py +280 -0
  67. apps/cli/commands/team.py +588 -0
  68. apps/cli/commands/team_render.py +8 -0
  69. apps/cli/commands/ui_cmds.py +358 -0
  70. apps/cli/commands/workflow_cmds.py +279 -0
  71. apps/cli/commands/workspace_cmds.py +1414 -0
  72. apps/cli/config_paths.py +70 -0
  73. apps/cli/config_store.py +61 -0
  74. apps/cli/deterministic.py +122 -0
  75. apps/cli/direct.py +48 -0
  76. apps/cli/github_app_auth.py +135 -0
  77. apps/cli/handlers/__init__.py +11 -0
  78. apps/cli/handlers/broker_handlers.py +122 -0
  79. apps/cli/handlers/chart_handlers.py +1309 -0
  80. apps/cli/handlers/market_handlers.py +2509 -0
  81. apps/cli/handlers/realty_handlers.py +114 -0
  82. apps/cli/handlers/strategy_advice.py +82 -0
  83. apps/cli/hooks.py +180 -0
  84. apps/cli/i18n.py +284 -0
  85. apps/cli/intent.py +136 -0
  86. apps/cli/intent_router.py +217 -0
  87. apps/cli/lifecycle_hooks.py +48 -0
  88. apps/cli/main.py +29 -0
  89. apps/cli/market_metadata.py +135 -0
  90. apps/cli/market_universe.py +265 -0
  91. apps/cli/message_processing.py +257 -0
  92. apps/cli/plan_mode.py +139 -0
  93. apps/cli/plotly_html.py +15 -0
  94. apps/cli/prediction_feedback.py +202 -0
  95. apps/cli/preflight.py +497 -0
  96. apps/cli/project_aria.py +60 -0
  97. apps/cli/prompts/__init__.py +0 -0
  98. apps/cli/prompts/coding.py +658 -0
  99. apps/cli/prompts/system_prompts.py +531 -0
  100. apps/cli/prompts/ui.py +434 -0
  101. apps/cli/providers/__init__.py +1 -0
  102. apps/cli/providers/base.py +271 -0
  103. apps/cli/providers/chat_routing.py +80 -0
  104. apps/cli/providers/llm/__init__.py +1 -0
  105. apps/cli/providers/llm/ollama_stream.py +1170 -0
  106. apps/cli/providers/llm/sse_stream.py +216 -0
  107. apps/cli/providers/runtime_bridge.py +185 -0
  108. apps/cli/runtime_consumer.py +489 -0
  109. apps/cli/session_export.py +87 -0
  110. apps/cli/session_jsonl.py +207 -0
  111. apps/cli/session_store.py +112 -0
  112. apps/cli/todo_tracker.py +190 -0
  113. apps/cli/tools/__init__.py +40 -0
  114. apps/cli/tools/context.py +46 -0
  115. apps/cli/tools/file_tools.py +112 -0
  116. apps/cli/tools/market_tools.py +549 -0
  117. apps/cli/tools/notebook_tools.py +111 -0
  118. apps/cli/tools/system_tools.py +669 -0
  119. apps/cli/tools/write_tools.py +715 -0
  120. apps/cli/tradingview_bridge.py +434 -0
  121. apps/cli/update_check.py +152 -0
  122. apps/cli/utils/__init__.py +0 -0
  123. apps/cli/utils/market_detect.py +1578 -0
  124. apps/daemon/README.md +14 -0
  125. apps/vscode/README.md +115 -0
  126. apps/vscode/package.json +70 -0
  127. aria_cli.py +11636 -0
  128. aria_code-4.1.3.dist-info/METADATA +952 -0
  129. aria_code-4.1.3.dist-info/RECORD +284 -0
  130. aria_code-4.1.3.dist-info/WHEEL +5 -0
  131. aria_code-4.1.3.dist-info/entry_points.txt +2 -0
  132. aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
  133. aria_code-4.1.3.dist-info/top_level.txt +50 -0
  134. aria_daemon.py +1295 -0
  135. aria_feishu_bot.py +1359 -0
  136. aria_relay_client.py +182 -0
  137. aria_relay_server.py +405 -0
  138. aria_telegram_bot.py +202 -0
  139. ariarc.py +328 -0
  140. artifacts.py +491 -0
  141. backtest_report.py +472 -0
  142. brokers/__init__.py +72 -0
  143. brokers/base.py +207 -0
  144. brokers/capabilities.py +264 -0
  145. brokers/cn/__init__.py +10 -0
  146. brokers/cn/easytrader_broker.py +193 -0
  147. brokers/cn/futu_broker.py +194 -0
  148. brokers/cn/longbridge_broker.py +190 -0
  149. brokers/cn/tiger_broker.py +196 -0
  150. brokers/cn/xtquant_broker.py +175 -0
  151. brokers/config.py +364 -0
  152. brokers/intl/__init__.py +5 -0
  153. brokers/intl/alpaca_broker.py +183 -0
  154. brokers/intl/ibkr_broker.py +215 -0
  155. brokers/intl/webull_broker.py +156 -0
  156. brokers/paper_broker.py +259 -0
  157. brokers/planning.py +296 -0
  158. brokers/registry.py +181 -0
  159. brokers/trading.py +237 -0
  160. change_store.py +127 -0
  161. command_safety.py +19 -0
  162. computer_use_tools.py +504 -0
  163. dashboard_generator.py +578 -0
  164. data_analysis_tools.py +808 -0
  165. data_cleaner.py +483 -0
  166. data_service.py +481 -0
  167. datasources/__init__.py +23 -0
  168. datasources/base.py +166 -0
  169. datasources/router.py +221 -0
  170. datasources/sources/__init__.py +15 -0
  171. datasources/sources/akshare_source.py +269 -0
  172. datasources/sources/alpha_vantage_source.py +202 -0
  173. datasources/sources/edgar_source.py +218 -0
  174. datasources/sources/finnhub_source.py +197 -0
  175. datasources/sources/fred_source.py +219 -0
  176. datasources/sources/tushare_source.py +141 -0
  177. datasources/sources/web_scraper_source.py +278 -0
  178. datasources/sources/world_bank_source.py +205 -0
  179. datasources/sources/yfinance_source.py +152 -0
  180. demo_player.py +204 -0
  181. doctor.py +508 -0
  182. file_analysis_tools.py +734 -0
  183. finance_formulas.py +389 -0
  184. football_data_client.py +1670 -0
  185. intent_classifier.py +358 -0
  186. local_finance_tools.py +3221 -0
  187. local_llm_provider.py +552 -0
  188. macro_tools.py +368 -0
  189. market_data_client.py +1899 -0
  190. mcp_client.py +506 -0
  191. memory_manager.py +245 -0
  192. model_capability.py +416 -0
  193. notification_tools.py +248 -0
  194. packages/__init__.py +23 -0
  195. packages/aria_agents/__init__.py +5 -0
  196. packages/aria_agents/manifest.py +69 -0
  197. packages/aria_core/__init__.py +34 -0
  198. packages/aria_core/architecture.py +192 -0
  199. packages/aria_core/export.py +124 -0
  200. packages/aria_core/manifest.py +65 -0
  201. packages/aria_infra/__init__.py +15 -0
  202. packages/aria_infra/arthera.py +52 -0
  203. packages/aria_infra/doctor.py +246 -0
  204. packages/aria_infra/product.py +37 -0
  205. packages/aria_mcp/__init__.py +25 -0
  206. packages/aria_mcp/bridge.py +38 -0
  207. packages/aria_mcp/config.py +97 -0
  208. packages/aria_mcp/tools.py +61 -0
  209. packages/aria_sdk/__init__.py +19 -0
  210. packages/aria_sdk/client.py +396 -0
  211. packages/aria_sdk/providers.py +70 -0
  212. packages/aria_sdk/streaming.py +73 -0
  213. packages/aria_sdk/types.py +86 -0
  214. packages/aria_services/__init__.py +55 -0
  215. packages/aria_services/context.py +258 -0
  216. packages/aria_services/data.py +11 -0
  217. packages/aria_services/provider_health.py +189 -0
  218. packages/aria_services/registry.py +213 -0
  219. packages/aria_services/usage.py +138 -0
  220. packages/aria_skills/__init__.py +5 -0
  221. packages/aria_skills/registry.py +59 -0
  222. packages/aria_tools/__init__.py +5 -0
  223. packages/aria_tools/registry.py +128 -0
  224. packages/quant_engine/__init__.py +6 -0
  225. packages/quant_engine/sports/__init__.py +72 -0
  226. packages/quant_engine/sports/calibrator.py +353 -0
  227. packages/quant_engine/sports/dixon_coles.py +234 -0
  228. packages/quant_engine/sports/elo.py +299 -0
  229. packages/quant_engine/sports/form.py +188 -0
  230. packages/quant_engine/sports/h2h.py +195 -0
  231. packages/quant_engine/sports/ml_model.py +354 -0
  232. packages/quant_engine/sports/predictor.py +311 -0
  233. packages/quant_engine/sports/tracker.py +664 -0
  234. packages/quant_engine/stochastic/__init__.py +27 -0
  235. packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  236. packages/quant_engine/stochastic/ito_calculus.py +477 -0
  237. packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  238. packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  239. packages/quant_engine/stochastic/options_pricing.py +573 -0
  240. packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  241. plan_utils.py +194 -0
  242. plugin_loader.py +328 -0
  243. portfolio_ledger.py +262 -0
  244. privacy/__init__.py +5 -0
  245. privacy/feedback.py +123 -0
  246. project_tools.py +525 -0
  247. providers/__init__.py +30 -0
  248. providers/llm/__init__.py +19 -0
  249. providers/llm/anthropic.py +184 -0
  250. providers/llm/base.py +139 -0
  251. providers/llm/ollama.py +128 -0
  252. providers/llm/openai_compat.py +282 -0
  253. providers/llm/registry.py +358 -0
  254. realty_data_tools.py +659 -0
  255. report_generator.py +1314 -0
  256. runtime/__init__.py +103 -0
  257. runtime/agent_loop.py +1183 -0
  258. runtime/approval.py +51 -0
  259. runtime/events.py +102 -0
  260. runtime/gateway.py +128 -0
  261. runtime/lsp.py +346 -0
  262. runtime/subagent.py +258 -0
  263. runtime/tool_executor.py +104 -0
  264. runtime/tool_policy.py +106 -0
  265. safety/__init__.py +21 -0
  266. safety/permissions.py +275 -0
  267. setup_wizard.py +653 -0
  268. strategy_vault.py +420 -0
  269. ui/__init__.py +100 -0
  270. ui/banner.py +310 -0
  271. ui/completer.py +391 -0
  272. ui/console.py +271 -0
  273. ui/image_render.py +243 -0
  274. ui/input_box.py +376 -0
  275. ui/picker.py +195 -0
  276. ui/render/__init__.py +11 -0
  277. ui/render/finance.py +1480 -0
  278. ui/render/market.py +225 -0
  279. ui/render/output.py +681 -0
  280. ui/render/team.py +346 -0
  281. ui/robot.py +235 -0
  282. workspace/__init__.py +6 -0
  283. workspace/files.py +170 -0
  284. workspace/verify.py +113 -0
@@ -0,0 +1,289 @@
1
+ """BusinessWorkflowCommandsMixin — research, earnings, realty, ops workflow commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class BusinessWorkflowCommandsMixin:
7
+ """Mixin: financial research and realty/operations workflow commands."""
8
+
9
+ async def cmd_research(self, args: str):
10
+ sym = args.strip().upper() or "AAPL"
11
+ # Route research through the deterministic team workflow so data
12
+ # fetching, rendering, and artifact saving happen in one service path.
13
+ await self.cmd_team(f"{sym} --full")
14
+
15
+ async def cmd_earnings_workflow(self, args: str):
16
+ parts = args.strip().split()
17
+ sym = parts[0].upper() if parts else "AAPL"
18
+ period = " ".join(parts[1:]) if len(parts) > 1 else "最近一个季度"
19
+ report_type = "deep" if any(k in period.lower() for k in ("deep", "深度", "全年", "年报", "10-k")) else "standard"
20
+ # Earnings review is a report workflow, not a code-generation prompt.
21
+ # The report command already fetches market data, records provenance,
22
+ # asks the model for the narrative, and saves the Markdown artifact.
23
+ await self.cmd_report(f"{sym} --format md --type {report_type}")
24
+
25
+ async def cmd_asset_diag(self, args: str):
26
+ asset_id = args.strip()
27
+ if not asset_id:
28
+ _p("用法: /asset-diag <资产ID或名称> 例: /asset-diag asset_000001", "dim")
29
+ return
30
+ asset_info = {}
31
+ api_url = self.terminal.config.get("api_url", "http://localhost:8000")
32
+ try:
33
+ import aiohttp
34
+ async with aiohttp.ClientSession() as sess:
35
+ async with sess.get(
36
+ f"{api_url}/api/realty/assets/{asset_id}",
37
+ timeout=aiohttp.ClientTimeout(total=5)
38
+ ) as resp:
39
+ if resp.status == 200:
40
+ body = await resp.json()
41
+ raw = body.get("data", {})
42
+ asset_info = {
43
+ "area": raw.get("area_sqm", 0),
44
+ "location": raw.get("address", asset_id),
45
+ "vacancy_days": raw.get("vacancy_days", 0),
46
+ "expected_rent": raw.get("monthly_rent_market", 0),
47
+ "allowed_business": raw.get("allowed_business_types", []),
48
+ "property_state": raw.get("property_state", "正常"),
49
+ "floor_height": raw.get("floor_height", 0),
50
+ }
51
+ _p(f"已从 API 加载资产: {raw.get('name', asset_id)}", "ok")
52
+ except Exception:
53
+ pass
54
+ if not asset_info:
55
+ _p("[dim]提示: 未找到资产数据,以 ID 作为位置标识演示(结果仅供参考)[/dim]")
56
+ asset_info = {
57
+ "location": asset_id,
58
+ "area": 0, "vacancy_days": 0,
59
+ "expected_rent": 0, "allowed_business": [],
60
+ "property_state": "正常",
61
+ }
62
+ await self._run_realty_agent("asset_diagnosis", asset_id, {"asset_info": asset_info})
63
+
64
+ async def cmd_contract_draft(self, args: str):
65
+ parts = args.split() if args else []
66
+ project_id = parts[0] if parts else "demo_project"
67
+ nego = {"guaranteed_amount": 0, "revenue_share_pct": 0}
68
+ for i, p in enumerate(parts):
69
+ if p == "--guaranteed" and i + 1 < len(parts):
70
+ try:
71
+ nego["guaranteed_amount"] = float(parts[i + 1])
72
+ except ValueError:
73
+ pass
74
+ elif p == "--share" and i + 1 < len(parts):
75
+ try:
76
+ nego["revenue_share_pct"] = float(parts[i + 1])
77
+ except ValueError:
78
+ pass
79
+ await self._run_realty_agent("contract_rules", project_id, {
80
+ "negotiation": nego,
81
+ "asset_info": {"name": project_id},
82
+ "operator_info": {},
83
+ })
84
+
85
+ async def cmd_revenue_calc(self, args: str):
86
+ parts = args.split() if args else []
87
+ if len(parts) < 2:
88
+ _p("用法: /revenue-calc <project_id> <总流水> [退款] 例: /revenue-calc proj_001 200000", "dim")
89
+ return
90
+ project_id = parts[0]
91
+ try:
92
+ gross = float(parts[1])
93
+ refunds = float(parts[2]) if len(parts) > 2 else 0.0
94
+ except ValueError:
95
+ _p("流水金额必须为数字", "error")
96
+ return
97
+ api_url = self.terminal.config.get("api_url", "http://localhost:8000")
98
+ rules = {}
99
+ try:
100
+ import aiohttp
101
+ async with aiohttp.ClientSession() as sess:
102
+ async with sess.get(f"{api_url}/api/realty/contracts/{project_id}",
103
+ timeout=aiohttp.ClientTimeout(total=5)) as resp:
104
+ if resp.status == 200:
105
+ body = await resp.json()
106
+ rules = body.get("data", {})
107
+ except Exception:
108
+ pass
109
+ if not rules:
110
+ _p(f"[dim]未找到 {project_id} 的合同规则,使用默认值演示[/dim]")
111
+ rules = {"guaranteed_monthly": 30000, "revenue_share_pct": 10,
112
+ "revenue_share_base": 0, "platform_fee_pct": 5,
113
+ "risk_reserve_pct": 3, "settlement_cycle": "monthly"}
114
+ await self._run_realty_agent("revenue_share", project_id, {
115
+ "contract_rules": rules,
116
+ "transaction_data": {"gross_revenue": gross, "refunds": refunds},
117
+ })
118
+
119
+ async def cmd_realty_risk_scan(self, args: str):
120
+ project_id = args.strip() or "demo_project"
121
+ if HAS_RICH:
122
+ console.print(f"\n [bold]风险扫描[/bold] 项目: [cyan]{project_id}[/cyan]")
123
+ api_url = self.terminal.config.get("api_url", "http://localhost:8000")
124
+ try:
125
+ import aiohttp
126
+ async with aiohttp.ClientSession() as sess:
127
+ async with sess.get(
128
+ f"{api_url}/api/realty/risks/scan/{project_id}",
129
+ timeout=aiohttp.ClientTimeout(total=10)
130
+ ) as resp:
131
+ if resp.status == 200:
132
+ data = await resp.json()
133
+ _print_risk_scan(data)
134
+ return
135
+ except Exception:
136
+ pass
137
+ await self._run_realty_team(["cashflow_verify", "energy_anomaly", "fulfillment_risk"], project_id, {})
138
+
139
+ async def cmd_ops_report(self, args: str):
140
+ project_id = args.strip() or "demo_project"
141
+ api_url = self.terminal.config.get("api_url", "http://localhost:8000")
142
+ project_info = {"name": project_id, "area": 0, "business_type": "未知"}
143
+ performance_data = {}
144
+ marketing_data = {}
145
+ try:
146
+ import aiohttp
147
+ async with aiohttp.ClientSession() as sess:
148
+ async with sess.get(
149
+ f"{api_url}/api/realty/assets/{project_id}",
150
+ timeout=aiohttp.ClientTimeout(total=5)
151
+ ) as resp:
152
+ if resp.status == 200:
153
+ raw = (await resp.json()).get("data", {})
154
+ project_info = {
155
+ "name": raw.get("name", project_id),
156
+ "area": raw.get("area_sqm", 0),
157
+ "business_type": raw.get("current_business_type", "未知"),
158
+ "open_date": raw.get("open_date", ""),
159
+ }
160
+ async with sess.get(
161
+ f"{api_url}/api/realty/revenue/splits?project_id={project_id}&page_size=3",
162
+ timeout=aiohttp.ClientTimeout(total=5)
163
+ ) as resp2:
164
+ if resp2.status == 200:
165
+ splits = (await resp2.json()).get("data", {}).get("splits", [])
166
+ if splits:
167
+ revenues = [s["split_result"].get("gross_revenue", 0) for s in splits]
168
+ avg_rev = sum(revenues) / len(revenues)
169
+ performance_data = {"monthly_revenue": avg_rev, "daily_visits": 0}
170
+ _p(f"已加载近 {len(splits)} 期分账数据,月均流水 {avg_rev:,.0f}元", "ok")
171
+ except Exception:
172
+ pass
173
+ if not performance_data:
174
+ _p("[dim]提示: 未找到运营数据,建议先录入分账记录后再运行此命令[/dim]")
175
+ await self._run_realty_agent("ops_optimize", project_id, {
176
+ "project_info": project_info,
177
+ "performance_data": performance_data,
178
+ "marketing_data": marketing_data,
179
+ "peer_benchmarks": {"revenue_per_sqm": 300},
180
+ })
181
+
182
+ async def cmd_exit_calc(self, args: str):
183
+ parts = args.split() if args else []
184
+ project_id = parts[0] if parts else "demo_project"
185
+ reason = "到期终止"
186
+ for i, p in enumerate(parts):
187
+ if p == "--reason" and i + 1 < len(parts):
188
+ reason = " ".join(parts[i + 1:])
189
+ break
190
+ api_url = self.terminal.config.get("api_url", "http://localhost:8000")
191
+ project_info = {"name": project_id}
192
+ financials = {"deposit_amount": 0, "unpaid_invoices": 0,
193
+ "guaranteed_monthly": 0, "exit_penalty_months": 3,
194
+ "prepayment_received": 0, "renovation_cost": 0}
195
+ try:
196
+ import aiohttp
197
+ async with aiohttp.ClientSession() as sess:
198
+ async with sess.get(
199
+ f"{api_url}/api/realty/contracts/{project_id}",
200
+ timeout=aiohttp.ClientTimeout(total=5)
201
+ ) as resp:
202
+ if resp.status == 200:
203
+ ctr = (await resp.json()).get("data", {})
204
+ from datetime import date
205
+ start = ctr.get("start_date", "")
206
+ used_months = 0
207
+ if start:
208
+ try:
209
+ from dateutil.relativedelta import relativedelta
210
+ d0 = date.fromisoformat(start)
211
+ delta = relativedelta(date.today(), d0)
212
+ used_months = delta.years * 12 + delta.months
213
+ except Exception:
214
+ pass
215
+ project_info.update({
216
+ "contract_years": ctr.get("contract_years", 1),
217
+ "used_months": used_months,
218
+ "contract_end": ctr.get("end_date", ""),
219
+ })
220
+ financials.update({
221
+ "deposit_amount": ctr.get("deposit_amount", 0),
222
+ "guaranteed_monthly": ctr.get("guaranteed_monthly", 0),
223
+ "exit_penalty_months": ctr.get("exit_penalty_months", 3),
224
+ })
225
+ _p(f"已加载合同规则: 保底 {ctr.get('guaranteed_monthly',0):,}元/月", "ok")
226
+ async with sess.get(
227
+ f"{api_url}/api/realty/invoices?project_id={project_id}&status=unpaid",
228
+ timeout=aiohttp.ClientTimeout(total=5)
229
+ ) as resp2:
230
+ if resp2.status == 200:
231
+ body2 = await resp2.json()
232
+ summary = body2.get("data", {}).get("summary", {})
233
+ unpaid = summary.get("total_amount", 0) - summary.get("paid_amount", 0)
234
+ financials["unpaid_invoices"] = unpaid
235
+ if unpaid > 0:
236
+ _p(f"发现未结账单合计: {unpaid:,.2f}元", "ok")
237
+ except Exception:
238
+ pass
239
+ await self._run_realty_agent("exit_settlement", project_id, {
240
+ "project_info": project_info,
241
+ "financials": financials,
242
+ "asset_condition": {},
243
+ "exit_reason": reason,
244
+ })
245
+
246
+ async def _run_realty_agent(self, agent_name: str, project_id: str, input_data: dict):
247
+ if HAS_RICH:
248
+ with console.status(f"[dim]运行 {agent_name} Agent...[/dim]", spinner="dots"):
249
+ result = await self._call_realty_agent(agent_name, project_id, input_data)
250
+ else:
251
+ print(f"Running {agent_name}...")
252
+ result = await self._call_realty_agent(agent_name, project_id, input_data)
253
+ if result:
254
+ _print_realty_result(result, agent_name)
255
+
256
+ async def _run_realty_team(self, agents: list, project_id: str, input_data: dict):
257
+ import asyncio
258
+ if HAS_RICH:
259
+ with console.status(f"[dim]并行扫描 {', '.join(agents)}...[/dim]", spinner="dots"):
260
+ tasks = [self._call_realty_agent(n, project_id, input_data) for n in agents]
261
+ results = await asyncio.gather(*tasks, return_exceptions=False)
262
+ else:
263
+ tasks = [self._call_realty_agent(n, project_id, input_data) for n in agents]
264
+ results = await asyncio.gather(*tasks, return_exceptions=False)
265
+ for res, name in zip(results, agents):
266
+ if res:
267
+ _print_realty_result(res, name)
268
+
269
+ async def _call_realty_agent(self, agent_name: str, project_id: str, input_data: dict):
270
+ try:
271
+ from agents.registry import get_registry
272
+ cls = get_registry().get(agent_name)
273
+ if not cls:
274
+ _p(f"Agent '{agent_name}' 未注册", "error")
275
+ return None
276
+ llm = None
277
+ try:
278
+ from providers.llm.registry import list_available_providers, get_provider
279
+ avail = [p for p in list_available_providers() if p.get("available")]
280
+ if avail:
281
+ llm = get_provider(avail[0]["name"])
282
+ except Exception:
283
+ pass
284
+ agent = cls(llm_provider=llm)
285
+ result = await agent.analyze(project_id, input_data)
286
+ return result
287
+ except Exception as e:
288
+ _p(f"Agent {agent_name} 执行失败: {e}", "error")
289
+ return None
@@ -0,0 +1,84 @@
1
+ """Shared CLI command metadata.
2
+
3
+ This module is intentionally UI-free so future channel adapters such as Feishu
4
+ or a local gateway can reuse the same command categories without importing the
5
+ large terminal implementation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from typing import FrozenSet, Tuple
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class DirectCommandSpec:
16
+ name: str
17
+ method_name: str
18
+ async_method: bool = False
19
+ watchable: bool = False
20
+ aliases: Tuple[str, ...] = ()
21
+
22
+ @property
23
+ def names(self) -> Tuple[str, ...]:
24
+ return (self.name, *self.aliases)
25
+
26
+
27
+ DIRECT_COMMANDS: Tuple[DirectCommandSpec, ...] = (
28
+ DirectCommandSpec("quote", "cmd_quote", async_method=True, watchable=True),
29
+ DirectCommandSpec("backtest", "cmd_backtest", async_method=True),
30
+ DirectCommandSpec("health", "cmd_health", async_method=True, watchable=True),
31
+ DirectCommandSpec("doctor", "cmd_doctor"),
32
+ DirectCommandSpec("tools", "cmd_tools"),
33
+ DirectCommandSpec("skills", "cmd_skills"),
34
+ DirectCommandSpec("sessions", "cmd_sessions"),
35
+ DirectCommandSpec("watch", "cmd_watch", aliases=("watchlist",)),
36
+ DirectCommandSpec("export", "cmd_export", async_method=True),
37
+ DirectCommandSpec("tv", "cmd_tv", async_method=True),
38
+ )
39
+
40
+
41
+ DIRECT_COMMAND_MAP = {
42
+ alias: spec
43
+ for spec in DIRECT_COMMANDS
44
+ for alias in spec.names
45
+ }
46
+
47
+
48
+ WATCHABLE_DIRECT_COMMANDS: FrozenSet[str] = frozenset(
49
+ alias
50
+ for spec in DIRECT_COMMANDS
51
+ if spec.watchable
52
+ for alias in spec.names
53
+ )
54
+
55
+
56
+ # Commands shown by default in /help. Other slash commands remain executable but
57
+ # are hidden to keep the startup surface compact.
58
+ VISIBLE_SLASH_COMMANDS: FrozenSet[str] = frozenset({
59
+ # Session
60
+ "/help", "/clear", "/compact", "/cost", "/status", "/health",
61
+ "/regen", "/undo", "/copy", "/recap", "/btw",
62
+ # Sessions
63
+ "/save", "/load", "/sessions", "/recall", "/export",
64
+ # Config
65
+ "/model", "/thinking", "/config", "/permissions", "/privacy",
66
+ # Setup & discovery
67
+ "/setup", "/apikey", "/doctor", "/architecture", "/mcp", "/skills", "/tools", "/packages",
68
+ # Auth
69
+ "/login", "/logout", "/whoami",
70
+ # Persistent data (direct writes)
71
+ "/alert", "/journal", "/watch", "/note", "/todo", "/memory",
72
+ # Broker
73
+ "/broker", "/account", "/positions", "/orders", "/paper", "/trade",
74
+ # Code & project
75
+ "/project", "/init", "/review", "/code", "/plan", "/run", "/tasks", "/completions", "/lsp",
76
+ # Research
77
+ "/team", "/deep",
78
+ # Quant
79
+ "/backtest", "/wf", "/tv",
80
+ # UI generation
81
+ "/ui",
82
+ # Other
83
+ "/artifacts", "/vision", "/upload-image", "/file", "/strategy", "/accuracy",
84
+ })