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.
- agents/__init__.py +32 -0
- agents/base.py +190 -0
- agents/deep/__init__.py +37 -0
- agents/deep/calibration_loop.py +144 -0
- agents/deep/critic.py +125 -0
- agents/deep/deepen.py +193 -0
- agents/deep/models.py +149 -0
- agents/deep/pipeline.py +164 -0
- agents/deep/quant_fusion.py +192 -0
- agents/deep/themes.py +95 -0
- agents/deep/tiers.py +106 -0
- agents/financial/__init__.py +10 -0
- agents/financial/catalyst.py +279 -0
- agents/financial/debate.py +145 -0
- agents/financial/earnings.py +303 -0
- agents/financial/fundamental.py +159 -0
- agents/financial/macro.py +99 -0
- agents/financial/news.py +207 -0
- agents/financial/risk.py +132 -0
- agents/financial/sector.py +279 -0
- agents/financial/synthesis.py +274 -0
- agents/financial/technical.py +258 -0
- agents/portfolio_agent.py +333 -0
- agents/realty/__init__.py +62 -0
- agents/realty/asset_diagnosis.py +150 -0
- agents/realty/business_match.py +165 -0
- agents/realty/cashflow_verify.py +208 -0
- agents/realty/contract_rules.py +209 -0
- agents/realty/energy_anomaly.py +188 -0
- agents/realty/exit_settlement.py +207 -0
- agents/realty/fulfillment_risk.py +205 -0
- agents/realty/ops_optimize.py +159 -0
- agents/realty/revenue_share.py +214 -0
- agents/registry.py +144 -0
- agents/sports/__init__.py +0 -0
- agents/sports/football_agent.py +169 -0
- agents/team.py +289 -0
- aliyun_data_client.py +660 -0
- apps/README.md +12 -0
- apps/__init__.py +2 -0
- apps/channels/README.md +15 -0
- apps/cli/README.md +13 -0
- apps/cli/__init__.py +2 -0
- apps/cli/bootstrap.py +99 -0
- apps/cli/codegen_paths.py +29 -0
- apps/cli/commands/__init__.py +16 -0
- apps/cli/commands/analysis_cmds.py +288 -0
- apps/cli/commands/backtest_cmds.py +1887 -0
- apps/cli/commands/broker_cmds.py +1154 -0
- apps/cli/commands/business_workflow_cmds.py +289 -0
- apps/cli/commands/catalog.py +84 -0
- apps/cli/commands/data_cmds.py +405 -0
- apps/cli/commands/diagnostic_cmds.py +179 -0
- apps/cli/commands/diagnostic_ops_cmds.py +696 -0
- apps/cli/commands/finance_render.py +12 -0
- apps/cli/commands/market.py +399 -0
- apps/cli/commands/market_cmds.py +1276 -0
- apps/cli/commands/market_context.py +425 -0
- apps/cli/commands/market_render.py +7 -0
- apps/cli/commands/model_cmds.py +1579 -0
- apps/cli/commands/ops_cmds.py +668 -0
- apps/cli/commands/portfolio_cmds.py +962 -0
- apps/cli/commands/report.py +377 -0
- apps/cli/commands/scaffold_templates.py +617 -0
- apps/cli/commands/session_cmds.py +179 -0
- apps/cli/commands/session_ux_cmds.py +280 -0
- apps/cli/commands/team.py +588 -0
- apps/cli/commands/team_render.py +8 -0
- apps/cli/commands/ui_cmds.py +358 -0
- apps/cli/commands/workflow_cmds.py +279 -0
- apps/cli/commands/workspace_cmds.py +1414 -0
- apps/cli/config_paths.py +70 -0
- apps/cli/config_store.py +61 -0
- apps/cli/deterministic.py +122 -0
- apps/cli/direct.py +48 -0
- apps/cli/github_app_auth.py +135 -0
- apps/cli/handlers/__init__.py +11 -0
- apps/cli/handlers/broker_handlers.py +122 -0
- apps/cli/handlers/chart_handlers.py +1309 -0
- apps/cli/handlers/market_handlers.py +2509 -0
- apps/cli/handlers/realty_handlers.py +114 -0
- apps/cli/handlers/strategy_advice.py +82 -0
- apps/cli/hooks.py +180 -0
- apps/cli/i18n.py +284 -0
- apps/cli/intent.py +136 -0
- apps/cli/intent_router.py +217 -0
- apps/cli/lifecycle_hooks.py +48 -0
- apps/cli/main.py +29 -0
- apps/cli/market_metadata.py +135 -0
- apps/cli/market_universe.py +265 -0
- apps/cli/message_processing.py +257 -0
- apps/cli/plan_mode.py +139 -0
- apps/cli/plotly_html.py +15 -0
- apps/cli/prediction_feedback.py +202 -0
- apps/cli/preflight.py +497 -0
- apps/cli/project_aria.py +60 -0
- apps/cli/prompts/__init__.py +0 -0
- apps/cli/prompts/coding.py +658 -0
- apps/cli/prompts/system_prompts.py +531 -0
- apps/cli/prompts/ui.py +434 -0
- apps/cli/providers/__init__.py +1 -0
- apps/cli/providers/base.py +271 -0
- apps/cli/providers/chat_routing.py +80 -0
- apps/cli/providers/llm/__init__.py +1 -0
- apps/cli/providers/llm/ollama_stream.py +1170 -0
- apps/cli/providers/llm/sse_stream.py +216 -0
- apps/cli/providers/runtime_bridge.py +185 -0
- apps/cli/runtime_consumer.py +489 -0
- apps/cli/session_export.py +87 -0
- apps/cli/session_jsonl.py +207 -0
- apps/cli/session_store.py +112 -0
- apps/cli/todo_tracker.py +190 -0
- apps/cli/tools/__init__.py +40 -0
- apps/cli/tools/context.py +46 -0
- apps/cli/tools/file_tools.py +112 -0
- apps/cli/tools/market_tools.py +549 -0
- apps/cli/tools/notebook_tools.py +111 -0
- apps/cli/tools/system_tools.py +669 -0
- apps/cli/tools/write_tools.py +715 -0
- apps/cli/tradingview_bridge.py +434 -0
- apps/cli/update_check.py +152 -0
- apps/cli/utils/__init__.py +0 -0
- apps/cli/utils/market_detect.py +1578 -0
- apps/daemon/README.md +14 -0
- apps/vscode/README.md +115 -0
- apps/vscode/package.json +70 -0
- aria_cli.py +11636 -0
- aria_code-4.1.3.dist-info/METADATA +952 -0
- aria_code-4.1.3.dist-info/RECORD +284 -0
- aria_code-4.1.3.dist-info/WHEEL +5 -0
- aria_code-4.1.3.dist-info/entry_points.txt +2 -0
- aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
- aria_code-4.1.3.dist-info/top_level.txt +50 -0
- aria_daemon.py +1295 -0
- aria_feishu_bot.py +1359 -0
- aria_relay_client.py +182 -0
- aria_relay_server.py +405 -0
- aria_telegram_bot.py +202 -0
- ariarc.py +328 -0
- artifacts.py +491 -0
- backtest_report.py +472 -0
- brokers/__init__.py +72 -0
- brokers/base.py +207 -0
- brokers/capabilities.py +264 -0
- brokers/cn/__init__.py +10 -0
- brokers/cn/easytrader_broker.py +193 -0
- brokers/cn/futu_broker.py +194 -0
- brokers/cn/longbridge_broker.py +190 -0
- brokers/cn/tiger_broker.py +196 -0
- brokers/cn/xtquant_broker.py +175 -0
- brokers/config.py +364 -0
- brokers/intl/__init__.py +5 -0
- brokers/intl/alpaca_broker.py +183 -0
- brokers/intl/ibkr_broker.py +215 -0
- brokers/intl/webull_broker.py +156 -0
- brokers/paper_broker.py +259 -0
- brokers/planning.py +296 -0
- brokers/registry.py +181 -0
- brokers/trading.py +237 -0
- change_store.py +127 -0
- command_safety.py +19 -0
- computer_use_tools.py +504 -0
- dashboard_generator.py +578 -0
- data_analysis_tools.py +808 -0
- data_cleaner.py +483 -0
- data_service.py +481 -0
- datasources/__init__.py +23 -0
- datasources/base.py +166 -0
- datasources/router.py +221 -0
- datasources/sources/__init__.py +15 -0
- datasources/sources/akshare_source.py +269 -0
- datasources/sources/alpha_vantage_source.py +202 -0
- datasources/sources/edgar_source.py +218 -0
- datasources/sources/finnhub_source.py +197 -0
- datasources/sources/fred_source.py +219 -0
- datasources/sources/tushare_source.py +141 -0
- datasources/sources/web_scraper_source.py +278 -0
- datasources/sources/world_bank_source.py +205 -0
- datasources/sources/yfinance_source.py +152 -0
- demo_player.py +204 -0
- doctor.py +508 -0
- file_analysis_tools.py +734 -0
- finance_formulas.py +389 -0
- football_data_client.py +1670 -0
- intent_classifier.py +358 -0
- local_finance_tools.py +3221 -0
- local_llm_provider.py +552 -0
- macro_tools.py +368 -0
- market_data_client.py +1899 -0
- mcp_client.py +506 -0
- memory_manager.py +245 -0
- model_capability.py +416 -0
- notification_tools.py +248 -0
- packages/__init__.py +23 -0
- packages/aria_agents/__init__.py +5 -0
- packages/aria_agents/manifest.py +69 -0
- packages/aria_core/__init__.py +34 -0
- packages/aria_core/architecture.py +192 -0
- packages/aria_core/export.py +124 -0
- packages/aria_core/manifest.py +65 -0
- packages/aria_infra/__init__.py +15 -0
- packages/aria_infra/arthera.py +52 -0
- packages/aria_infra/doctor.py +246 -0
- packages/aria_infra/product.py +37 -0
- packages/aria_mcp/__init__.py +25 -0
- packages/aria_mcp/bridge.py +38 -0
- packages/aria_mcp/config.py +97 -0
- packages/aria_mcp/tools.py +61 -0
- packages/aria_sdk/__init__.py +19 -0
- packages/aria_sdk/client.py +396 -0
- packages/aria_sdk/providers.py +70 -0
- packages/aria_sdk/streaming.py +73 -0
- packages/aria_sdk/types.py +86 -0
- packages/aria_services/__init__.py +55 -0
- packages/aria_services/context.py +258 -0
- packages/aria_services/data.py +11 -0
- packages/aria_services/provider_health.py +189 -0
- packages/aria_services/registry.py +213 -0
- packages/aria_services/usage.py +138 -0
- packages/aria_skills/__init__.py +5 -0
- packages/aria_skills/registry.py +59 -0
- packages/aria_tools/__init__.py +5 -0
- packages/aria_tools/registry.py +128 -0
- packages/quant_engine/__init__.py +6 -0
- packages/quant_engine/sports/__init__.py +72 -0
- packages/quant_engine/sports/calibrator.py +353 -0
- packages/quant_engine/sports/dixon_coles.py +234 -0
- packages/quant_engine/sports/elo.py +299 -0
- packages/quant_engine/sports/form.py +188 -0
- packages/quant_engine/sports/h2h.py +195 -0
- packages/quant_engine/sports/ml_model.py +354 -0
- packages/quant_engine/sports/predictor.py +311 -0
- packages/quant_engine/sports/tracker.py +664 -0
- packages/quant_engine/stochastic/__init__.py +27 -0
- packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- packages/quant_engine/stochastic/ito_calculus.py +477 -0
- packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- packages/quant_engine/stochastic/options_pricing.py +573 -0
- packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- plan_utils.py +194 -0
- plugin_loader.py +328 -0
- portfolio_ledger.py +262 -0
- privacy/__init__.py +5 -0
- privacy/feedback.py +123 -0
- project_tools.py +525 -0
- providers/__init__.py +30 -0
- providers/llm/__init__.py +19 -0
- providers/llm/anthropic.py +184 -0
- providers/llm/base.py +139 -0
- providers/llm/ollama.py +128 -0
- providers/llm/openai_compat.py +282 -0
- providers/llm/registry.py +358 -0
- realty_data_tools.py +659 -0
- report_generator.py +1314 -0
- runtime/__init__.py +103 -0
- runtime/agent_loop.py +1183 -0
- runtime/approval.py +51 -0
- runtime/events.py +102 -0
- runtime/gateway.py +128 -0
- runtime/lsp.py +346 -0
- runtime/subagent.py +258 -0
- runtime/tool_executor.py +104 -0
- runtime/tool_policy.py +106 -0
- safety/__init__.py +21 -0
- safety/permissions.py +275 -0
- setup_wizard.py +653 -0
- strategy_vault.py +420 -0
- ui/__init__.py +100 -0
- ui/banner.py +310 -0
- ui/completer.py +391 -0
- ui/console.py +271 -0
- ui/image_render.py +243 -0
- ui/input_box.py +376 -0
- ui/picker.py +195 -0
- ui/render/__init__.py +11 -0
- ui/render/finance.py +1480 -0
- ui/render/market.py +225 -0
- ui/render/output.py +681 -0
- ui/render/team.py +346 -0
- ui/robot.py +235 -0
- workspace/__init__.py +6 -0
- workspace/files.py +170 -0
- workspace/verify.py +113 -0
setup_wizard.py
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
setup_wizard.py — Aria Code 首次配置向导
|
|
4
|
+
==========================================
|
|
5
|
+
运行方式:
|
|
6
|
+
python3 setup_wizard.py # 完整向导
|
|
7
|
+
python3 setup_wizard.py --model # 仅配置模型
|
|
8
|
+
python3 setup_wizard.py --feishu # 仅配置飞书
|
|
9
|
+
python3 setup_wizard.py --telegram # 仅配置 Telegram
|
|
10
|
+
|
|
11
|
+
向导完成后会生成:
|
|
12
|
+
~/.aria/.env 环境变量配置
|
|
13
|
+
~/.aria/config.json Aria Code 配置
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import platform
|
|
22
|
+
import subprocess
|
|
23
|
+
import sys
|
|
24
|
+
import time
|
|
25
|
+
import uuid
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
# ── 尝试用 rich 渲染,回落到纯文本 ──────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
from rich.console import Console
|
|
32
|
+
from rich.panel import Panel
|
|
33
|
+
from rich.prompt import Confirm, Prompt
|
|
34
|
+
from rich.table import Table
|
|
35
|
+
from rich import box as rbox
|
|
36
|
+
_rich = True
|
|
37
|
+
console = Console()
|
|
38
|
+
except ImportError:
|
|
39
|
+
_rich = False
|
|
40
|
+
class _FakeConsole:
|
|
41
|
+
def print(self, *a, **k): print(*a)
|
|
42
|
+
def rule(self, *a, **k): print("─" * 60)
|
|
43
|
+
console = _FakeConsole() # type: ignore
|
|
44
|
+
|
|
45
|
+
_ARIA_DIR = Path.home() / ".aria"
|
|
46
|
+
_ENV_FILE = _ARIA_DIR / ".env"
|
|
47
|
+
_CFG_FILE = _ARIA_DIR / "config.json"
|
|
48
|
+
|
|
49
|
+
# ── System language detection ─────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
from apps.cli.i18n import detect_system_lang as _detect_lang
|
|
53
|
+
_SYS_LANG = _detect_lang()
|
|
54
|
+
except Exception:
|
|
55
|
+
_SYS_LANG = "en"
|
|
56
|
+
|
|
57
|
+
# Bilingual wizard strings — keyed by (_SYS_LANG, key)
|
|
58
|
+
_WZ: dict[str, dict[str, str]] = {
|
|
59
|
+
"setup_model_title": {"zh": "本地模型配置", "en": "Local Model Setup"},
|
|
60
|
+
"ollama_not_found": {"zh": "未检测到 Ollama", "en": "Ollama not found"},
|
|
61
|
+
"install_ollama_q": {"zh": "是否自动安装 Ollama?", "en": "Auto-install Ollama?"},
|
|
62
|
+
"install_ollama_ing": {"zh": "正在安装 Ollama...", "en": "Installing Ollama..."},
|
|
63
|
+
"install_ollama_fail": {"zh": "Ollama 安装失败,请手动安装: https://ollama.com",
|
|
64
|
+
"en": "Ollama install failed. Visit https://ollama.com"},
|
|
65
|
+
"install_ollama_ok": {"zh": "Ollama 安装完成", "en": "Ollama installed"},
|
|
66
|
+
"skip_ollama": {"zh": "跳过 Ollama 安装。你也可以配置 API 模型(下方步骤)。",
|
|
67
|
+
"en": "Skipping Ollama. You can configure a cloud API model below."},
|
|
68
|
+
"win_ollama_hint": {"zh": "请访问 https://ollama.com/download 下载 Windows 版本后重新运行向导。",
|
|
69
|
+
"en": "Download the Windows installer from https://ollama.com/download and re-run."},
|
|
70
|
+
"download_model": {"zh": "正在下载模型 {name},可能需要几分钟...",
|
|
71
|
+
"en": "Downloading {name}, this may take a few minutes..."},
|
|
72
|
+
"select_model_prompt": {"zh": "选择模型(输入序号/名称,直接回车使用 {default})",
|
|
73
|
+
"en": "Select model (number/name, Enter to use {default})"},
|
|
74
|
+
"select_model_no_local": {"zh": "选择模型(输入序号 1-6 或完整名称)",
|
|
75
|
+
"en": "Select model (enter number 1-6 or full name)"},
|
|
76
|
+
"model_set": {"zh": "已选择模型: {name}", "en": "Model set: {name}"},
|
|
77
|
+
"pull_model_q": {"zh": "是否立即下载?", "en": "Download now?"},
|
|
78
|
+
"pull_skip": {"zh": "跳过下载,请稍后运行: ollama pull {name}",
|
|
79
|
+
"en": "Skipping download. Run later: ollama pull {name}"},
|
|
80
|
+
"lang_detected": {"zh": "检测到系统语言:中文(可用 /config set ui_lang=en 切换)",
|
|
81
|
+
"en": "Detected system language: English (use /config set ui_lang=zh to switch)"},
|
|
82
|
+
"api_keys_section": {"zh": "API 密钥配置(可选)", "en": "API Keys (Optional)"},
|
|
83
|
+
"api_keys_info": {"zh": "配置后可使用云端模型(Claude / GPT-4 / DeepSeek 等)",
|
|
84
|
+
"en": "Configure to use cloud models (Claude / GPT-4 / DeepSeek etc.)"},
|
|
85
|
+
"api_key_prompt": {"zh": "配置 {name} API Key{suffix}?",
|
|
86
|
+
"en": "Configure {name} API Key{suffix}?"},
|
|
87
|
+
"api_key_saved": {"zh": "{name} API Key 已保存", "en": "{name} API Key saved"},
|
|
88
|
+
"api_keys_done": {"zh": "共配置 {count} 个 API Key",
|
|
89
|
+
"en": "{count} API key(s) configured"},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _wz(key: str, **kwargs) -> str:
|
|
94
|
+
"""Look up wizard string in detected system language."""
|
|
95
|
+
entry = _WZ.get(key, {})
|
|
96
|
+
tmpl = entry.get(_SYS_LANG) or entry.get("en") or key
|
|
97
|
+
return tmpl.format(**kwargs) if kwargs else tmpl
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# ── Helpers ──────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
def _ask(prompt: str, default: str = "", password: bool = False) -> str:
|
|
103
|
+
if _rich:
|
|
104
|
+
return Prompt.ask(prompt, default=default, password=password)
|
|
105
|
+
d = f" [{default}]" if default else ""
|
|
106
|
+
val = input(f"{prompt}{d}: ").strip()
|
|
107
|
+
return val or default
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _confirm(prompt: str, default: bool = True) -> bool:
|
|
111
|
+
if _rich:
|
|
112
|
+
return Confirm.ask(prompt, default=default)
|
|
113
|
+
ans = input(f"{prompt} [{'Y/n' if default else 'y/N'}]: ").strip().lower()
|
|
114
|
+
return (ans != "n") if default else (ans == "y")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _section(title: str) -> None:
|
|
118
|
+
if _rich:
|
|
119
|
+
console.rule(f"[bold cyan]{title}[/bold cyan]")
|
|
120
|
+
else:
|
|
121
|
+
print(f"\n{'─'*20} {title} {'─'*20}")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _ok(msg: str) -> None: console.print(f"[green]✓[/green] {msg}" if _rich else f"✓ {msg}")
|
|
125
|
+
def _warn(msg: str) -> None: console.print(f"[yellow]⚠[/yellow] {msg}" if _rich else f"⚠ {msg}")
|
|
126
|
+
def _err(msg: str) -> None: console.print(f"[red]✗[/red] {msg}" if _rich else f"✗ {msg}")
|
|
127
|
+
def _info(msg: str) -> None: console.print(f"[dim]{msg}[/dim]" if _rich else msg)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _load_env() -> dict[str, str]:
|
|
131
|
+
env: dict[str, str] = {}
|
|
132
|
+
if _ENV_FILE.exists():
|
|
133
|
+
for line in _ENV_FILE.read_text().splitlines():
|
|
134
|
+
line = line.strip()
|
|
135
|
+
if line and not line.startswith("#") and "=" in line:
|
|
136
|
+
k, _, v = line.partition("=")
|
|
137
|
+
env[k.strip()] = v.strip()
|
|
138
|
+
return env
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _save_env(env: dict[str, str]) -> None:
|
|
142
|
+
_ARIA_DIR.mkdir(parents=True, exist_ok=True)
|
|
143
|
+
lines = ["# Aria Code 配置 — 由 setup_wizard.py 生成\n"]
|
|
144
|
+
for k, v in sorted(env.items()):
|
|
145
|
+
lines.append(f"{k}={v}\n")
|
|
146
|
+
_ENV_FILE.write_text("".join(lines))
|
|
147
|
+
_ENV_FILE.chmod(0o600)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# ── Step 1: Ollama check + model selection ───────────────────────────────────
|
|
151
|
+
|
|
152
|
+
def _ollama_installed() -> bool:
|
|
153
|
+
return subprocess.run(["which", "ollama"], capture_output=True).returncode == 0
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _ollama_models() -> list[str]:
|
|
157
|
+
try:
|
|
158
|
+
result = subprocess.run(
|
|
159
|
+
["ollama", "list"], capture_output=True, text=True, timeout=10
|
|
160
|
+
)
|
|
161
|
+
lines = result.stdout.strip().splitlines()[1:] # skip header
|
|
162
|
+
return [l.split()[0] for l in lines if l.strip()]
|
|
163
|
+
except Exception:
|
|
164
|
+
return []
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _install_ollama() -> bool:
|
|
168
|
+
_info(_wz("install_ollama_ing"))
|
|
169
|
+
system = platform.system()
|
|
170
|
+
if system == "Darwin":
|
|
171
|
+
if subprocess.run(["which", "brew"], capture_output=True).returncode == 0:
|
|
172
|
+
ret = subprocess.run(["brew", "install", "ollama"]).returncode
|
|
173
|
+
return ret == 0
|
|
174
|
+
cmd = 'curl -fsSL https://ollama.com/install.sh | sh'
|
|
175
|
+
elif system == "Linux":
|
|
176
|
+
cmd = 'curl -fsSL https://ollama.com/install.sh | sh'
|
|
177
|
+
elif system == "Windows":
|
|
178
|
+
_warn(_wz("win_ollama_hint"))
|
|
179
|
+
return False
|
|
180
|
+
else:
|
|
181
|
+
_warn(f"Unknown system {system}. Install manually: https://ollama.com")
|
|
182
|
+
return False
|
|
183
|
+
return subprocess.run(cmd, shell=True).returncode == 0
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _pull_model(name: str) -> bool:
|
|
187
|
+
_info(_wz("download_model", name=name))
|
|
188
|
+
return subprocess.run(["ollama", "pull", name]).returncode == 0
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
_RECOMMENDED_MODELS_ZH = [
|
|
192
|
+
("qwen2.5:7b", "阿里通义千问 7B · 中文最强 · ~4GB · 推荐"),
|
|
193
|
+
("deepseek-r1:7b", "DeepSeek R1 7B · 推理强 · ~4GB"),
|
|
194
|
+
("llama3.2:3b", "Meta Llama 3.2 3B · 速度快 · ~2GB"),
|
|
195
|
+
("mistral:7b", "Mistral 7B · 均衡 · ~4GB"),
|
|
196
|
+
("qwen2.5:14b", "通义千问 14B · 质量高 · ~8GB · 需 16GB RAM"),
|
|
197
|
+
("deepseek-r1:14b", "DeepSeek R1 14B · 推理最强 · ~8GB · 需 16GB RAM"),
|
|
198
|
+
]
|
|
199
|
+
_RECOMMENDED_MODELS_EN = [
|
|
200
|
+
("qwen2.5:7b", "Qwen2.5 7B · Best Chinese+English · ~4GB · Recommended"),
|
|
201
|
+
("deepseek-r1:7b", "DeepSeek R1 7B · Strong reasoning · ~4GB"),
|
|
202
|
+
("llama3.2:3b", "Meta Llama 3.2 3B · Fast, lightweight · ~2GB"),
|
|
203
|
+
("mistral:7b", "Mistral 7B · Balanced · ~4GB"),
|
|
204
|
+
("qwen2.5:14b", "Qwen2.5 14B · High quality · ~8GB · Needs 16GB RAM"),
|
|
205
|
+
("deepseek-r1:14b", "DeepSeek R1 14B · Best reasoning · ~8GB · Needs 16GB RAM"),
|
|
206
|
+
]
|
|
207
|
+
_RECOMMENDED_MODELS = _RECOMMENDED_MODELS_ZH if _SYS_LANG == "zh" else _RECOMMENDED_MODELS_EN
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def setup_model(env: dict[str, str]) -> None:
|
|
211
|
+
_section(_wz("setup_model_title"))
|
|
212
|
+
|
|
213
|
+
if not _ollama_installed():
|
|
214
|
+
_warn(_wz("ollama_not_found"))
|
|
215
|
+
if _confirm(_wz("install_ollama_q")):
|
|
216
|
+
if not _install_ollama():
|
|
217
|
+
_err(_wz("install_ollama_fail"))
|
|
218
|
+
return
|
|
219
|
+
_ok(_wz("install_ollama_ok"))
|
|
220
|
+
else:
|
|
221
|
+
_info(_wz("skip_ollama"))
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
existing = _ollama_models()
|
|
225
|
+
_installed_label = "✓ installed" if _SYS_LANG != "zh" else "✓ 已安装"
|
|
226
|
+
_other_label = "Other installed model" if _SYS_LANG != "zh" else "已安装的其他模型"
|
|
227
|
+
|
|
228
|
+
if _rich:
|
|
229
|
+
_col_model = "Model" if _SYS_LANG != "zh" else "模型"
|
|
230
|
+
_col_desc = "Description" if _SYS_LANG != "zh" else "说明"
|
|
231
|
+
t = Table(box=rbox.SIMPLE, show_header=True)
|
|
232
|
+
t.add_column("#", style="dim", width=3)
|
|
233
|
+
t.add_column(_col_model, style="cyan")
|
|
234
|
+
t.add_column(_col_desc, style="dim")
|
|
235
|
+
for i, (name, desc) in enumerate(_RECOMMENDED_MODELS, 1):
|
|
236
|
+
marker = f" [green]{_installed_label}[/green]" if name in existing else ""
|
|
237
|
+
t.add_row(str(i), name + marker, desc)
|
|
238
|
+
if existing:
|
|
239
|
+
for m in existing:
|
|
240
|
+
if not any(m == n for n, _ in _RECOMMENDED_MODELS):
|
|
241
|
+
t.add_row("*", m, _other_label)
|
|
242
|
+
console.print(t)
|
|
243
|
+
else:
|
|
244
|
+
for i, (name, desc) in enumerate(_RECOMMENDED_MODELS, 1):
|
|
245
|
+
installed = f" [{_installed_label}]" if name in existing else ""
|
|
246
|
+
print(f" {i}. {name}{installed} — {desc}")
|
|
247
|
+
|
|
248
|
+
if existing:
|
|
249
|
+
default_model = existing[0]
|
|
250
|
+
choice = _ask(_wz("select_model_prompt", default=default_model), default=default_model)
|
|
251
|
+
else:
|
|
252
|
+
choice = _ask(_wz("select_model_no_local"), default="1")
|
|
253
|
+
|
|
254
|
+
# Resolve number → model name
|
|
255
|
+
try:
|
|
256
|
+
idx = int(choice) - 1
|
|
257
|
+
if 0 <= idx < len(_RECOMMENDED_MODELS):
|
|
258
|
+
model_name = _RECOMMENDED_MODELS[idx][0]
|
|
259
|
+
else:
|
|
260
|
+
model_name = choice
|
|
261
|
+
except ValueError:
|
|
262
|
+
model_name = choice
|
|
263
|
+
|
|
264
|
+
if model_name not in existing:
|
|
265
|
+
_pull_q = (f"模型 {model_name} 未安装,是否立即下载?" if _SYS_LANG == "zh"
|
|
266
|
+
else f"Model {model_name} not installed. Download now?")
|
|
267
|
+
if _confirm(_pull_q):
|
|
268
|
+
if _pull_model(model_name):
|
|
269
|
+
_ok(f"模型 {model_name} 下载完成" if _SYS_LANG == "zh"
|
|
270
|
+
else f"Model {model_name} downloaded")
|
|
271
|
+
else:
|
|
272
|
+
_err(_wz("pull_skip", name=model_name))
|
|
273
|
+
else:
|
|
274
|
+
_warn(_wz("pull_skip", name=model_name))
|
|
275
|
+
|
|
276
|
+
env["ARIA_DEFAULT_MODEL"] = model_name
|
|
277
|
+
env["OLLAMA_BASE_URL"] = "http://localhost:11434"
|
|
278
|
+
_ok(_wz("model_set", name=model_name))
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
# ── Step 2: API keys (optional) ──────────────────────────────────────────────
|
|
282
|
+
|
|
283
|
+
def setup_api_keys(env: dict[str, str]) -> None:
|
|
284
|
+
_section(_wz("api_keys_section"))
|
|
285
|
+
_info(_wz("api_keys_info"))
|
|
286
|
+
|
|
287
|
+
# (display_name, env_var, hint)
|
|
288
|
+
_LLM_PROVIDERS = [
|
|
289
|
+
# ── 国际
|
|
290
|
+
("Anthropic (Claude)", "ANTHROPIC_API_KEY", "claude.ai/settings/keys"),
|
|
291
|
+
("OpenAI (GPT-4o / Whisper)", "OPENAI_API_KEY", "platform.openai.com/api-keys"),
|
|
292
|
+
("DeepSeek", "DEEPSEEK_API_KEY", "platform.deepseek.com"),
|
|
293
|
+
("Google Gemini", "GOOGLE_API_KEY", "aistudio.google.com/apikey"),
|
|
294
|
+
("xAI Grok", "XAI_API_KEY", "console.x.ai"),
|
|
295
|
+
("Groq (fast inference)", "GROQ_API_KEY", "console.groq.com/keys"),
|
|
296
|
+
("Mistral AI", "MISTRAL_API_KEY", "console.mistral.ai/api-keys"),
|
|
297
|
+
("Cohere", "COHERE_API_KEY", "dashboard.cohere.com/api-keys"),
|
|
298
|
+
("Perplexity", "PERPLEXITY_API_KEY", "perplexity.ai/settings/api"),
|
|
299
|
+
("Together AI", "TOGETHER_API_KEY", "api.together.xyz/settings/api-keys"),
|
|
300
|
+
# ── 国内
|
|
301
|
+
("SiliconFlow 硅基流动", "SILICONFLOW_API_KEY", "cloud.siliconflow.cn"),
|
|
302
|
+
("DashScope 阿里云百炼", "DASHSCOPE_API_KEY", "dashscope.aliyuncs.com"),
|
|
303
|
+
("Moonshot Kimi 月之暗面", "MOONSHOT_API_KEY", "platform.moonshot.cn/api-keys"),
|
|
304
|
+
("Zhipu GLM 智谱", "ZHIPU_API_KEY", "open.bigmodel.cn"),
|
|
305
|
+
("Baidu ERNIE 百度千帆", "QIANFAN_ACCESS_KEY", "console.bce.baidu.com/iam/#/iam/accesslist"),
|
|
306
|
+
("ByteDance Doubao 字节豆包", "ARK_API_KEY", "console.volcengine.com/ark"),
|
|
307
|
+
("MiniMax", "MINIMAX_API_KEY", "platform.minimaxi.com"),
|
|
308
|
+
("StepFun 阶跃星辰", "STEPFUN_API_KEY", "platform.stepfun.com"),
|
|
309
|
+
("01.AI Yi 零一万物", "ONEAI_API_KEY", "platform.lingyiwanwu.com"),
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
_configured = []
|
|
313
|
+
for name, env_var, hint in _LLM_PROVIDERS:
|
|
314
|
+
already = bool(env.get(env_var) or __import__("os").getenv(env_var))
|
|
315
|
+
suffix = f" [已配置]" if already else ""
|
|
316
|
+
prompt = _wz("api_key_prompt", name=name, suffix=suffix)
|
|
317
|
+
if _confirm(prompt, default=False):
|
|
318
|
+
_info(f" → {hint}")
|
|
319
|
+
key = _ask(env_var, password=True)
|
|
320
|
+
if key:
|
|
321
|
+
env[env_var] = key
|
|
322
|
+
_configured.append(name)
|
|
323
|
+
_ok(_wz("api_key_saved", name=name))
|
|
324
|
+
|
|
325
|
+
if _configured:
|
|
326
|
+
_ok(_wz("api_keys_done", count=len(_configured)))
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# ── Step 3: Feishu connection ─────────────────────────────────────────────────
|
|
330
|
+
|
|
331
|
+
def setup_feishu(env: dict[str, str]) -> None:
|
|
332
|
+
_section("飞书连接配置")
|
|
333
|
+
|
|
334
|
+
if _rich:
|
|
335
|
+
console.print(Panel(
|
|
336
|
+
"[bold]两种连接方式:[/bold]\n\n"
|
|
337
|
+
"[cyan]1. Aria 中继服务[/cyan] [dim](推荐)[/dim]\n"
|
|
338
|
+
" • 无需创建飞书应用\n"
|
|
339
|
+
" • 在飞书向 Aria 官方 Bot 发送绑定码即可\n"
|
|
340
|
+
" • 消息经中继服务器转发到你的本机\n\n"
|
|
341
|
+
"[cyan]2. 自建飞书应用[/cyan] [dim](完全自主)[/dim]\n"
|
|
342
|
+
" • 需要飞书开发者账号\n"
|
|
343
|
+
" • 消息直接推送到你的服务器\n"
|
|
344
|
+
" • 适合有公网 IP / 服务器的用户",
|
|
345
|
+
title="飞书连接方式",
|
|
346
|
+
border_style="cyan",
|
|
347
|
+
))
|
|
348
|
+
else:
|
|
349
|
+
print(" 1. Aria 中继服务(推荐,无需创建飞书应用)")
|
|
350
|
+
print(" 2. 自建飞书应用(需要飞书开发者账号)")
|
|
351
|
+
|
|
352
|
+
choice = _ask("选择方式", default="1")
|
|
353
|
+
|
|
354
|
+
if choice == "1":
|
|
355
|
+
_setup_feishu_relay(env)
|
|
356
|
+
else:
|
|
357
|
+
_setup_feishu_own_app(env)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _setup_feishu_relay(env: dict[str, str]) -> None:
|
|
361
|
+
"""Connect via Aria Relay Server — no Feishu developer account needed."""
|
|
362
|
+
relay_url = env.get("ARIA_RELAY_URL", "wss://relay.aria.ai")
|
|
363
|
+
relay_url = _ask("中继服务器地址", default=relay_url)
|
|
364
|
+
env["ARIA_RELAY_URL"] = relay_url
|
|
365
|
+
env["ARIA_RELAY_MODE"] = "relay"
|
|
366
|
+
|
|
367
|
+
# Generate or reuse client_id
|
|
368
|
+
client_id = env.get("ARIA_RELAY_CLIENT_ID") or f"aria-{uuid.uuid4().hex[:12]}"
|
|
369
|
+
env["ARIA_RELAY_CLIENT_ID"] = client_id
|
|
370
|
+
|
|
371
|
+
if _rich:
|
|
372
|
+
console.print(Panel(
|
|
373
|
+
f"[bold]你的绑定码:[/bold]\n\n"
|
|
374
|
+
f"[bold yellow on black] ARIA-BIND-{client_id.upper()} [/bold yellow on black]\n\n"
|
|
375
|
+
f"[dim]操作步骤:[/dim]\n"
|
|
376
|
+
f"1. 在飞书搜索 [cyan]Aria Bot[/cyan] 并添加好友\n"
|
|
377
|
+
f"2. 发送:[cyan]/bind ARIA-BIND-{client_id.upper()}[/cyan]\n"
|
|
378
|
+
f"3. 收到 [green]\"绑定成功\"[/green] 后,飞书消息将转发到你的 Aria\n\n"
|
|
379
|
+
f"[dim](中继客户端会在 daemon 启动时自动连接)[/dim]",
|
|
380
|
+
title="📱 飞书绑定步骤",
|
|
381
|
+
border_style="yellow",
|
|
382
|
+
))
|
|
383
|
+
else:
|
|
384
|
+
print(f"\n你的绑定码: ARIA-BIND-{client_id.upper()}")
|
|
385
|
+
print("1. 在飞书搜索 Aria Bot 并添加好友")
|
|
386
|
+
print(f"2. 发送: /bind ARIA-BIND-{client_id.upper()}")
|
|
387
|
+
print("3. 收到绑定成功后继续\n")
|
|
388
|
+
|
|
389
|
+
if _confirm("是否已完成绑定?(可以稍后运行向导再完成)", default=False):
|
|
390
|
+
_ok("飞书中继绑定完成")
|
|
391
|
+
else:
|
|
392
|
+
_warn("请完成绑定后再启动 daemon,否则飞书消息无法到达本机")
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _setup_feishu_own_app(env: dict[str, str]) -> None:
|
|
396
|
+
"""Self-hosted Feishu app configuration with step-by-step guidance."""
|
|
397
|
+
if _rich:
|
|
398
|
+
console.print(
|
|
399
|
+
"\n[dim]飞书开发者后台:https://open.feishu.cn/app[/dim]\n"
|
|
400
|
+
"步骤:创建自建应用 → 凭证与基本信息 → 复制 App ID 和 App Secret\n"
|
|
401
|
+
)
|
|
402
|
+
else:
|
|
403
|
+
print("飞书开发者后台:https://open.feishu.cn/app")
|
|
404
|
+
|
|
405
|
+
app_id = _ask("FEISHU_APP_ID (如: cli_xxxxxxxxxxxx)", default=env.get("FEISHU_APP_ID", ""))
|
|
406
|
+
secret = _ask("FEISHU_APP_SECRET", default=env.get("FEISHU_APP_SECRET", ""), password=True)
|
|
407
|
+
|
|
408
|
+
if app_id:
|
|
409
|
+
env["FEISHU_APP_ID"] = app_id
|
|
410
|
+
if secret:
|
|
411
|
+
env["FEISHU_APP_SECRET"] = secret
|
|
412
|
+
env["ARIA_RELAY_MODE"] = "own_app"
|
|
413
|
+
|
|
414
|
+
# Webhook URL guidance
|
|
415
|
+
_info("\n事件订阅 Request URL: http://<你的IP或域名>/api/v1/feishu/event")
|
|
416
|
+
_info("如果没有公网 IP,可以用 cloudflared 建立隧道:")
|
|
417
|
+
_info(" brew install cloudflared && cloudflared tunnel --url http://localhost:8000")
|
|
418
|
+
|
|
419
|
+
if _confirm("是否配置 FEISHU_WEBHOOK_URL(群机器人单向推送)?", default=False):
|
|
420
|
+
wh = _ask("FEISHU_WEBHOOK_URL")
|
|
421
|
+
if wh:
|
|
422
|
+
env["FEISHU_WEBHOOK_URL"] = wh
|
|
423
|
+
_ok("飞书 Webhook 已保存")
|
|
424
|
+
|
|
425
|
+
_ok("飞书自建应用配置完成")
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
# ── Step 4: Telegram Bot ─────────────────────────────────────────────────────
|
|
429
|
+
|
|
430
|
+
def setup_telegram(env: dict[str, str]) -> None:
|
|
431
|
+
_section("Telegram Bot 配置")
|
|
432
|
+
|
|
433
|
+
if _rich:
|
|
434
|
+
console.print(Panel(
|
|
435
|
+
"[bold]三步完成 Telegram Bot 接入:[/bold]\n\n"
|
|
436
|
+
"[cyan]第一步:创建 Bot,获取 Token[/cyan]\n"
|
|
437
|
+
" 1. 打开 Telegram,搜索并进入 [bold]@BotFather[/bold]\n"
|
|
438
|
+
" 2. 发送命令:/newbot\n"
|
|
439
|
+
" 3. 按提示填写 Bot 名称(如 Aria Alerts)和用户名(需以 bot 结尾)\n"
|
|
440
|
+
" 4. 复制 BotFather 回复中的 [bold]Token[/bold]\n"
|
|
441
|
+
" 格式:[dim]1234567890:AAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx[/dim]\n\n"
|
|
442
|
+
"[cyan]第二步:获取你的 Chat ID[/cyan]\n"
|
|
443
|
+
" 1. 搜索并进入 [bold]@userinfobot[/bold]\n"
|
|
444
|
+
" 2. 发送任意消息,复制返回的 [bold]Id[/bold] 数字\n"
|
|
445
|
+
" 3. 多个用户用英文逗号分隔,如:[dim]123456,789012[/dim]\n\n"
|
|
446
|
+
"[cyan]第三步:启动 Bot[/cyan]\n"
|
|
447
|
+
" • 运行 [bold]python3 aria_daemon.py[/bold]\n"
|
|
448
|
+
" • 在 Telegram 向你的 Bot 发送 /start\n"
|
|
449
|
+
" • 支持命令:/price AAPL · /brief · /alerts · /help",
|
|
450
|
+
title="🤖 Telegram Bot 配置指南",
|
|
451
|
+
border_style="cyan",
|
|
452
|
+
))
|
|
453
|
+
else:
|
|
454
|
+
print("\n=== Telegram Bot 配置 ===")
|
|
455
|
+
print("步骤:")
|
|
456
|
+
print(" 1. Telegram → 搜索 @BotFather → /newbot → 复制 Token")
|
|
457
|
+
print(" 2. 搜索 @userinfobot → 发消息 → 复制 Id 数字")
|
|
458
|
+
print(" 3. 填入下方后运行: python3 aria_daemon.py")
|
|
459
|
+
|
|
460
|
+
# ── 获取 Token ────────────────────────────────────────────────────────────
|
|
461
|
+
existing_token = env.get("TELEGRAM_BOT_TOKEN", "")
|
|
462
|
+
if existing_token and existing_token != "your_bot_token_here":
|
|
463
|
+
_info(f"当前 Token: {existing_token[:10]}…(留空保留)")
|
|
464
|
+
token = _ask("TELEGRAM_BOT_TOKEN", default=existing_token)
|
|
465
|
+
|
|
466
|
+
if not token or token == "your_bot_token_here":
|
|
467
|
+
_warn("未填写 Token,Telegram Bot 不会启动")
|
|
468
|
+
return
|
|
469
|
+
|
|
470
|
+
# ── 验证 Token ────────────────────────────────────────────────────────────
|
|
471
|
+
_info("验证 Token 中…")
|
|
472
|
+
bot_info = _verify_telegram_token(token)
|
|
473
|
+
if bot_info:
|
|
474
|
+
_ok(f"Token 有效!Bot 名称: @{bot_info.get('username', '?')}")
|
|
475
|
+
else:
|
|
476
|
+
_warn("Token 验证失败(可能是网络问题),继续保存但请检查 Token 是否正确")
|
|
477
|
+
|
|
478
|
+
env["TELEGRAM_BOT_TOKEN"] = token
|
|
479
|
+
|
|
480
|
+
# ── 获取 Chat ID ─────────────────────────────────────────────────────────
|
|
481
|
+
existing_ids = env.get("TELEGRAM_ALLOWED_IDS", "")
|
|
482
|
+
ids_str = _ask(
|
|
483
|
+
"TELEGRAM_ALLOWED_IDS (你的 Chat ID,多个用英文逗号分隔)",
|
|
484
|
+
default=existing_ids if existing_ids != "123456789" else "",
|
|
485
|
+
)
|
|
486
|
+
if ids_str:
|
|
487
|
+
env["TELEGRAM_ALLOWED_IDS"] = ids_str
|
|
488
|
+
_ok(f"已允许 Chat ID: {ids_str}")
|
|
489
|
+
else:
|
|
490
|
+
_warn("未填写 Chat ID,Bot 将拒绝所有消息")
|
|
491
|
+
_info("获取方法:Telegram 搜索 @userinfobot 发送任意消息即可")
|
|
492
|
+
|
|
493
|
+
_ok("Telegram Bot 配置完成")
|
|
494
|
+
if _rich:
|
|
495
|
+
console.print(
|
|
496
|
+
"\n [dim]启动 daemon 后向你的 Bot 发 /start 即可开始使用[/dim]\n"
|
|
497
|
+
" [cyan]python3 aria_daemon.py[/cyan]\n"
|
|
498
|
+
)
|
|
499
|
+
else:
|
|
500
|
+
print("\n 启动: python3 aria_daemon.py")
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _verify_telegram_token(token: str) -> dict | None:
|
|
504
|
+
"""Call getMe to verify a Telegram bot token. Returns bot info dict or None."""
|
|
505
|
+
import urllib.request as _req
|
|
506
|
+
import urllib.error as _err
|
|
507
|
+
try:
|
|
508
|
+
url = f"https://api.telegram.org/bot{token}/getMe"
|
|
509
|
+
with _req.urlopen(url, timeout=5) as resp:
|
|
510
|
+
import json as _j
|
|
511
|
+
data = _j.loads(resp.read())
|
|
512
|
+
if data.get("ok"):
|
|
513
|
+
return data.get("result", {})
|
|
514
|
+
except (_err.URLError, Exception):
|
|
515
|
+
pass
|
|
516
|
+
return None
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
# ── Step 5: Write config & finalize ──────────────────────────────────────────
|
|
520
|
+
|
|
521
|
+
def _write_aria_config(env: dict[str, str]) -> None:
|
|
522
|
+
"""Write ~/.aria/config.json for aria_cli.py to read."""
|
|
523
|
+
cfg: dict = {}
|
|
524
|
+
if _CFG_FILE.exists():
|
|
525
|
+
try:
|
|
526
|
+
cfg = json.loads(_CFG_FILE.read_text())
|
|
527
|
+
except Exception:
|
|
528
|
+
cfg = {}
|
|
529
|
+
|
|
530
|
+
model = env.get("ARIA_DEFAULT_MODEL", "")
|
|
531
|
+
if model:
|
|
532
|
+
cfg["default_model"] = model
|
|
533
|
+
cfg["model"] = model # also write as "model" for aria_cli.py
|
|
534
|
+
|
|
535
|
+
# Persist detected system language so UI renders correctly from first launch
|
|
536
|
+
cfg["ui_lang"] = _SYS_LANG
|
|
537
|
+
|
|
538
|
+
_ARIA_DIR.mkdir(parents=True, exist_ok=True)
|
|
539
|
+
_CFG_FILE.write_text(json.dumps(cfg, indent=2, ensure_ascii=False))
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _print_summary(env: dict[str, str]) -> None:
|
|
543
|
+
_section("配置完成")
|
|
544
|
+
if _rich:
|
|
545
|
+
t = Table(box=rbox.SIMPLE, show_header=False)
|
|
546
|
+
t.add_column("项目", style="dim", width=22)
|
|
547
|
+
t.add_column("值", style="cyan")
|
|
548
|
+
|
|
549
|
+
def _mask(v: str) -> str:
|
|
550
|
+
return v[:6] + "…" + v[-4:] if len(v) > 12 else "***"
|
|
551
|
+
|
|
552
|
+
t.add_row("默认模型", env.get("ARIA_DEFAULT_MODEL", "—"))
|
|
553
|
+
t.add_row("飞书模式", env.get("ARIA_RELAY_MODE", "—"))
|
|
554
|
+
if env.get("ARIA_RELAY_CLIENT_ID"):
|
|
555
|
+
t.add_row("中继 Client ID", env["ARIA_RELAY_CLIENT_ID"])
|
|
556
|
+
if env.get("FEISHU_APP_ID"):
|
|
557
|
+
t.add_row("飞书 App ID", env["FEISHU_APP_ID"])
|
|
558
|
+
tg_token = env.get("TELEGRAM_BOT_TOKEN", "")
|
|
559
|
+
if tg_token and tg_token != "your_bot_token_here":
|
|
560
|
+
t.add_row("Telegram Bot", _mask(tg_token))
|
|
561
|
+
if env.get("TELEGRAM_ALLOWED_IDS"):
|
|
562
|
+
t.add_row("Telegram IDs", env["TELEGRAM_ALLOWED_IDS"])
|
|
563
|
+
if env.get("ANTHROPIC_API_KEY"):
|
|
564
|
+
t.add_row("Anthropic Key", _mask(env["ANTHROPIC_API_KEY"]))
|
|
565
|
+
if env.get("OPENAI_API_KEY"):
|
|
566
|
+
t.add_row("OpenAI Key", _mask(env["OPENAI_API_KEY"]))
|
|
567
|
+
console.print(t)
|
|
568
|
+
|
|
569
|
+
console.print(Panel(
|
|
570
|
+
"[bold green]Aria Code 配置成功![/bold green]\n\n"
|
|
571
|
+
"启动方式:\n"
|
|
572
|
+
" [cyan]python3 aria_cli.py[/cyan] # 交互终端\n"
|
|
573
|
+
" [cyan]python3 aria_daemon.py --install[/cyan] # 安装为系统服务(开机自启)\n"
|
|
574
|
+
" [cyan]python3 aria_daemon.py[/cyan] # 前台运行 daemon\n\n"
|
|
575
|
+
"[dim]配置文件: ~/.aria/.env[/dim]",
|
|
576
|
+
border_style="green",
|
|
577
|
+
))
|
|
578
|
+
else:
|
|
579
|
+
print("\n配置完成!")
|
|
580
|
+
print(f"默认模型: {env.get('ARIA_DEFAULT_MODEL', '—')}")
|
|
581
|
+
print(f"飞书模式: {env.get('ARIA_RELAY_MODE', '—')}")
|
|
582
|
+
print("\n启动: python3 aria_daemon.py")
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
# ── Main ─────────────────────────────────────────────────────────────────────
|
|
586
|
+
|
|
587
|
+
def main() -> None:
|
|
588
|
+
parser = argparse.ArgumentParser(description="Aria Code Setup Wizard / 配置向导")
|
|
589
|
+
parser.add_argument("--model", action="store_true", help="Model setup only / 仅配置模型")
|
|
590
|
+
parser.add_argument("--feishu", action="store_true", help="Feishu only / 仅配置飞书")
|
|
591
|
+
parser.add_argument("--telegram", action="store_true", help="Telegram only / 仅配置 Telegram")
|
|
592
|
+
parser.add_argument("--keys", action="store_true", help="API keys only / 仅配置 API Key")
|
|
593
|
+
args = parser.parse_args()
|
|
594
|
+
|
|
595
|
+
# Show detected language
|
|
596
|
+
_info(_wz("lang_detected"))
|
|
597
|
+
|
|
598
|
+
if _SYS_LANG == "zh":
|
|
599
|
+
_intro = (
|
|
600
|
+
"[bold cyan]Aria Code[/bold cyan] [dim]— AI 金融终端[/dim]\n\n"
|
|
601
|
+
"本向导将帮助你完成首次配置:\n"
|
|
602
|
+
" • 本地 AI 模型(Ollama)\n"
|
|
603
|
+
" • API 密钥(Claude / GPT-4,可选)\n"
|
|
604
|
+
" • 飞书机器人连接\n"
|
|
605
|
+
" • Telegram Bot 接入"
|
|
606
|
+
)
|
|
607
|
+
_title = "🔧 首次配置向导"
|
|
608
|
+
_plain_intro = "\n=== Aria Code 配置向导 ===\n"
|
|
609
|
+
else:
|
|
610
|
+
_intro = (
|
|
611
|
+
"[bold cyan]Aria Code[/bold cyan] [dim]— AI Finance Terminal[/dim]\n\n"
|
|
612
|
+
"This wizard will help you set up:\n"
|
|
613
|
+
" • Local AI model (Ollama)\n"
|
|
614
|
+
" • API keys (Claude / GPT-4 — optional)\n"
|
|
615
|
+
" • Feishu / Lark bot connection\n"
|
|
616
|
+
" • Telegram Bot integration"
|
|
617
|
+
)
|
|
618
|
+
_title = "🔧 First-Run Setup"
|
|
619
|
+
_plain_intro = "\n=== Aria Code Setup Wizard ===\n"
|
|
620
|
+
|
|
621
|
+
if _rich:
|
|
622
|
+
console.print(Panel(_intro, title=_title, border_style="cyan"))
|
|
623
|
+
else:
|
|
624
|
+
print(_plain_intro)
|
|
625
|
+
|
|
626
|
+
env = _load_env()
|
|
627
|
+
run_all = not (args.model or args.feishu or args.telegram or args.keys)
|
|
628
|
+
|
|
629
|
+
if run_all or args.model:
|
|
630
|
+
setup_model(env)
|
|
631
|
+
|
|
632
|
+
if run_all or args.keys:
|
|
633
|
+
setup_api_keys(env)
|
|
634
|
+
|
|
635
|
+
if run_all or args.feishu:
|
|
636
|
+
setup_feishu(env)
|
|
637
|
+
|
|
638
|
+
if run_all or args.telegram:
|
|
639
|
+
setup_telegram(env)
|
|
640
|
+
|
|
641
|
+
_save_env(env)
|
|
642
|
+
_write_aria_config(env)
|
|
643
|
+
_ok(f"配置已保存到 {_ENV_FILE}")
|
|
644
|
+
|
|
645
|
+
if run_all:
|
|
646
|
+
_print_summary(env)
|
|
647
|
+
|
|
648
|
+
if run_all and _confirm("\n是否立即启动 Aria Daemon?", default=True):
|
|
649
|
+
os.execv(sys.executable, [sys.executable, str(Path(__file__).parent / "aria_daemon.py")])
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
if __name__ == "__main__":
|
|
653
|
+
main()
|