MintOS 0.1.1__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.
- mintos-0.1.1.dist-info/METADATA +58 -0
- mintos-0.1.1.dist-info/RECORD +28 -0
- mintos-0.1.1.dist-info/WHEEL +5 -0
- mintos-0.1.1.dist-info/entry_points.txt +2 -0
- mintos-0.1.1.dist-info/top_level.txt +1 -0
- mos/__init__.py +3 -0
- mos/cli/__init__.py +0 -0
- mos/cli/config.py +222 -0
- mos/cli/init.py +229 -0
- mos/cli/mcp.py +13 -0
- mos/cli/mos.py +43 -0
- mos/cli/plugin.py +125 -0
- mos/core/__init__.py +0 -0
- mos/core/baseconfig.py +196 -0
- mos/core/config.py +131 -0
- mos/core/dataflow/__init__.py +0 -0
- mos/core/dataflow/flat_json_model.py +141 -0
- mos/core/dataflow/types.py +126 -0
- mos/core/grafana/__init__.py +36 -0
- mos/core/grafana/manage.py +460 -0
- mos/core/influxdb/__init__.py +0 -0
- mos/core/influxdb/v3.py +0 -0
- mos/core/llm/__init__.py +0 -0
- mos/core/llm/openai_compatible_api.py +516 -0
- mos/core/logging.py +48 -0
- mos/core/mcp.py +4 -0
- mos/core/plugin.py +811 -0
- mos/core/resource.py +32 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: MintOS
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Mind OS or Money OS
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/tkorays/MOS
|
|
7
|
+
Project-URL: Issues, https://github.com/tkorays/MOS/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.13
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: python-dotenv>=1.2.1
|
|
13
|
+
Requires-Dist: loguru
|
|
14
|
+
Requires-Dist: pydantic-settings
|
|
15
|
+
Requires-Dist: pydantic
|
|
16
|
+
Requires-Dist: requests>=2.32.0
|
|
17
|
+
Requires-Dist: click>=8.4.1
|
|
18
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
19
|
+
Requires-Dist: mcp>=1.28.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
22
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
23
|
+
Requires-Dist: ruff; extra == "dev"
|
|
24
|
+
|
|
25
|
+
# MOS
|
|
26
|
+
|
|
27
|
+
MOS 是一个基于 Python 的插件化框架,提供核心基础设施和插件管理能力。
|
|
28
|
+
|
|
29
|
+
## 核心特性
|
|
30
|
+
|
|
31
|
+
- **插件架构**:基于 Python entry_points 的标准插件机制
|
|
32
|
+
- **依赖隔离**:每个插件独立管理依赖,主仓库只包含核心框架
|
|
33
|
+
- **灵活安装**:插件可通过 pip 安装,支持从 Git、PyPI 或本地安装
|
|
34
|
+
|
|
35
|
+
## 可用插件
|
|
36
|
+
|
|
37
|
+
MOS 通过插件扩展功能,目前已有的插件:
|
|
38
|
+
|
|
39
|
+
| 插件 | 功能 | 安装方式 |
|
|
40
|
+
|------|------|----------|
|
|
41
|
+
| **mos-quant** | 量化交易框架,支持数据采集、回测、实盘交易 | `pip install mos-quant` |
|
|
42
|
+
| **mos-wiki** | 知识库管理系统,支持 Markdown 知识库管理 | `pip install mos-wiki` |
|
|
43
|
+
| **mos-agent** | 智能体系统,支持 LLM Agent 开发 | `pip install mos-agent` |
|
|
44
|
+
|
|
45
|
+
## 快速开始
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# 安装核心框架
|
|
49
|
+
pip install mos
|
|
50
|
+
|
|
51
|
+
# 安装插件(从 GitHub)
|
|
52
|
+
pip install git+https://github.com/yourname/mos_quant.git
|
|
53
|
+
pip install git+https://github.com/yourname/mos_wiki.git
|
|
54
|
+
pip install git+https://github.com/yourname/mos_agent.git
|
|
55
|
+
|
|
56
|
+
# 查看已加载插件
|
|
57
|
+
mos plugin list
|
|
58
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
mos/__init__.py,sha256=qKlfXlBbeex2PhIWuyLo4vst8I-53KBCWXAjXnkOq9w,39
|
|
2
|
+
mos/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
mos/cli/config.py,sha256=McS6hdiRLGKl9d86YtLGblND1b1d9vGdfPBRhvzDnN8,7149
|
|
4
|
+
mos/cli/init.py,sha256=cKaTjfIhobiWLyGucdVCzj0ZmsJhsQ1VRgPRA6Me0WY,6652
|
|
5
|
+
mos/cli/mcp.py,sha256=Jq-KUy01pBz2itynmqp7bh33TNJg5RXBAcXCeyr85S4,271
|
|
6
|
+
mos/cli/mos.py,sha256=HRFVReZSrOTRGy_-4tYjMJaX0YJAdhiSS3KMD0anI34,1171
|
|
7
|
+
mos/cli/plugin.py,sha256=7w0ygOduG_Hjx0bB2wtYRs5vywiR5Yy_pqt6Ae7yDBk,4066
|
|
8
|
+
mos/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
mos/core/baseconfig.py,sha256=2O8IfPsexoyq9OZDMzn6C_0d_xQMxShk9bsmOJRbrhA,7477
|
|
10
|
+
mos/core/config.py,sha256=oTwl7vqkVTXzyHvhkmvYANk72ehpY0GI7jADc3Kr_Go,4302
|
|
11
|
+
mos/core/logging.py,sha256=VIU1OoG4q_3Rsffz_mAjuE8jKHlABMEcR02o2i719MU,1231
|
|
12
|
+
mos/core/mcp.py,sha256=2KXhUHchw5CyBS119NETvkWc6EKLkdMpZv-nV9-meps,89
|
|
13
|
+
mos/core/plugin.py,sha256=GcL7BgFwLFFVAhgIcdoHkC0D4SVMK4QvOex9aPhnPBA,28457
|
|
14
|
+
mos/core/resource.py,sha256=rQwQ5gFdPLuHNV_aPEjhWqKdcWyjjsU-oDc5Jr6ecRo,1117
|
|
15
|
+
mos/core/dataflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
mos/core/dataflow/flat_json_model.py,sha256=Wga71tEu3Uk1gAcRxIeB2Hp3v9NyUj0jqSbyv6z-fts,5512
|
|
17
|
+
mos/core/dataflow/types.py,sha256=Dyr91NzxOSCjKaVjgFoNyQEWp10952ecTMDa7Leq6Og,3123
|
|
18
|
+
mos/core/grafana/__init__.py,sha256=5VsssC6P27gEK7eUTDOUBUV6lVCZct5T6BidAglKLHs,850
|
|
19
|
+
mos/core/grafana/manage.py,sha256=jSNQrjZZOeP-5A4Ai13bt-lPrE43jJXP938ZtCa8vFo,15497
|
|
20
|
+
mos/core/influxdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
mos/core/influxdb/v3.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
mos/core/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
mos/core/llm/openai_compatible_api.py,sha256=QkH54H4GxD3E7S6Q3SVHGP9jkZTmCXxJHUgJedud07g,18689
|
|
24
|
+
mintos-0.1.1.dist-info/METADATA,sha256=dV1k3E8nr7oCeRVgHyhduzHzJpoh6NZL7ucO_Y7KWgo,1862
|
|
25
|
+
mintos-0.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
26
|
+
mintos-0.1.1.dist-info/entry_points.txt,sha256=m13wVRUum4BygNy_kmo4wm_IVGlpAb1R7lQTvU5buyg,40
|
|
27
|
+
mintos-0.1.1.dist-info/top_level.txt,sha256=yHW4Fvt1SB0qOfsMARJMTbafOyckDeaxTpCNRLIGA4k,4
|
|
28
|
+
mintos-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mos
|
mos/__init__.py
ADDED
mos/cli/__init__.py
ADDED
|
File without changes
|
mos/cli/config.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
from mos.core.config import get_config, reload_config
|
|
8
|
+
from mos.core.plugin import get_registry
|
|
9
|
+
from mos.core.logging import get_logger
|
|
10
|
+
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group()
|
|
15
|
+
def config():
|
|
16
|
+
"""配置管理命令"""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _get_config_funcs(config_type: str) -> tuple[Callable, Callable]:
|
|
21
|
+
"""根据配置类型获取对应的配置管理函数
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
config_type: 配置类型,'main' 表示主配置,其他为插件名称
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
(get_config_func, reload_config_func)
|
|
28
|
+
"""
|
|
29
|
+
if config_type == "main":
|
|
30
|
+
return get_config, reload_config
|
|
31
|
+
|
|
32
|
+
registry = get_registry()
|
|
33
|
+
get_func = registry.get_config_func(config_type)
|
|
34
|
+
reload_func = registry.get_reload_func(config_type)
|
|
35
|
+
|
|
36
|
+
if get_func is None or reload_func is None:
|
|
37
|
+
raise click.ClickException(f"未知的配置类型: '{config_type}'。可用类型: main, {', '.join(registry.list_names())}")
|
|
38
|
+
|
|
39
|
+
return get_func, reload_func
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _format_value(value):
|
|
43
|
+
"""格式化配置值为可读字符串"""
|
|
44
|
+
value_type = type(value)
|
|
45
|
+
|
|
46
|
+
if value_type is bool:
|
|
47
|
+
return "true" if value else "false"
|
|
48
|
+
elif value_type in (list, tuple):
|
|
49
|
+
return ", ".join(str(item) for item in value)
|
|
50
|
+
else:
|
|
51
|
+
return str(value)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _print_config_tree(data, prefix=""):
|
|
55
|
+
"""递归打印配置树"""
|
|
56
|
+
data_type = type(data)
|
|
57
|
+
|
|
58
|
+
if data_type is not dict:
|
|
59
|
+
click.echo(f"{prefix}{data}")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
for key, value in data.items():
|
|
63
|
+
value_type = type(value)
|
|
64
|
+
|
|
65
|
+
if value is None:
|
|
66
|
+
click.echo(f"{prefix}{key}: null")
|
|
67
|
+
elif value_type is dict:
|
|
68
|
+
click.echo(f"{prefix}{key}:")
|
|
69
|
+
_print_config_tree(value, prefix + " ")
|
|
70
|
+
else:
|
|
71
|
+
display_value = _format_value(value)
|
|
72
|
+
click.echo(f"{prefix}{key}: {display_value}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _coerce_cli_value(value: str) -> Any:
|
|
76
|
+
"""根据 CLI 字符串字面量推断 Python 标量(true/false/int/float/JSON/str)。"""
|
|
77
|
+
if value.startswith("~"):
|
|
78
|
+
return value
|
|
79
|
+
lowered = value.lower()
|
|
80
|
+
if lowered == "true":
|
|
81
|
+
return True
|
|
82
|
+
if lowered == "false":
|
|
83
|
+
return False
|
|
84
|
+
if value.isdigit():
|
|
85
|
+
return int(value)
|
|
86
|
+
try:
|
|
87
|
+
return float(value)
|
|
88
|
+
except ValueError:
|
|
89
|
+
if value.startswith("[") or value.startswith("{"):
|
|
90
|
+
return json.loads(value)
|
|
91
|
+
return value
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@click.command()
|
|
95
|
+
@click.option('--type', 'config_type', default='main', help='配置类型:main(主配置) 或插件名称(如 quant, wiki)')
|
|
96
|
+
def list(config_type: str):
|
|
97
|
+
"""列出所有配置项
|
|
98
|
+
|
|
99
|
+
示例:
|
|
100
|
+
mos config list # 列出主配置
|
|
101
|
+
mos config list --type quant # 列出 quant 插件配置
|
|
102
|
+
mos config list --type wiki # 列出 wiki 插件配置
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
get_config_func, _ = _get_config_funcs(config_type)
|
|
106
|
+
config_obj = get_config_func()
|
|
107
|
+
config_dict = config_obj.model_dump()
|
|
108
|
+
config_file = config_obj.config_file_path
|
|
109
|
+
|
|
110
|
+
click.echo(f"当前配置 ({config_type}):\n")
|
|
111
|
+
click.echo(f"配置文件: {config_file}")
|
|
112
|
+
click.echo(f"配置文件存在: {config_file.exists()}")
|
|
113
|
+
click.echo()
|
|
114
|
+
|
|
115
|
+
_print_config_tree(config_dict)
|
|
116
|
+
|
|
117
|
+
click.echo()
|
|
118
|
+
click.echo("提示: 使用 'mos config set <key> <value> --type <type>' 修改配置")
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.error(f"列出配置失败: {e}")
|
|
122
|
+
raise click.ClickException(f"列出配置失败: {e}")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@click.command()
|
|
126
|
+
@click.argument("key")
|
|
127
|
+
@click.argument("value")
|
|
128
|
+
@click.option('--type', 'config_type', default='main', help='配置类型:main(主配置) 或插件名称(如 quant, wiki)')
|
|
129
|
+
def set(key, value, config_type: str):
|
|
130
|
+
"""设置配置项
|
|
131
|
+
|
|
132
|
+
示例:
|
|
133
|
+
mos config set log.level DEBUG
|
|
134
|
+
mos config set debug true --type main
|
|
135
|
+
mos config set database.path ~/.mos/custom_data/ --type quant
|
|
136
|
+
mos config set mini_qmt.path D:\\QMT --type quant
|
|
137
|
+
"""
|
|
138
|
+
try:
|
|
139
|
+
get_config_func, reload_config_func = _get_config_funcs(config_type)
|
|
140
|
+
keys = key.split(".")
|
|
141
|
+
if not keys or not all(keys):
|
|
142
|
+
raise click.ClickException(f"无效的 key: {key!r}")
|
|
143
|
+
|
|
144
|
+
# 用当前 Config 作为基线,把 CLI 改的字段 deep-merge 进去。
|
|
145
|
+
# pydantic 在 model_validate 阶段会做类型转换(str→int/bool/...)。
|
|
146
|
+
current = get_config_func()
|
|
147
|
+
override: dict[str, Any] = {}
|
|
148
|
+
cursor: dict[str, Any] = override
|
|
149
|
+
for k in keys[:-1]:
|
|
150
|
+
cursor[k] = {}
|
|
151
|
+
cursor = cursor[k]
|
|
152
|
+
cursor[keys[-1]] = _coerce_cli_value(value)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
new_cfg = current.update(**override)
|
|
156
|
+
except ValidationError as e:
|
|
157
|
+
raise click.ClickException(f"配置项 {key!r} 校验失败: {e}")
|
|
158
|
+
|
|
159
|
+
target = new_cfg.save()
|
|
160
|
+
reload_config_func()
|
|
161
|
+
|
|
162
|
+
click.echo(f"[OK] 已设置 {config_type}.{key} = {value}")
|
|
163
|
+
click.echo(f" 配置文件: {target}")
|
|
164
|
+
click.echo()
|
|
165
|
+
click.echo("提示: 某些配置可能需要重启应用才能生效")
|
|
166
|
+
|
|
167
|
+
except click.ClickException:
|
|
168
|
+
raise
|
|
169
|
+
except Exception as e:
|
|
170
|
+
logger.error(f"设置配置失败: {e}")
|
|
171
|
+
raise click.ClickException(f"设置配置失败: {e}")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@click.command()
|
|
175
|
+
@click.argument("key")
|
|
176
|
+
@click.option('--type', 'config_type', default='main', help='配置类型:main(主配置) 或插件名称(如 quant, wiki)')
|
|
177
|
+
def get(key, config_type: str):
|
|
178
|
+
"""获取指定配置项的值
|
|
179
|
+
|
|
180
|
+
示例:
|
|
181
|
+
mos config get log.level
|
|
182
|
+
mos config get debug --type main
|
|
183
|
+
mos config get database.path --type quant
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
get_config_func, _ = _get_config_funcs(config_type)
|
|
187
|
+
config_obj = get_config_func()
|
|
188
|
+
value = config_obj.get(*key.split("."))
|
|
189
|
+
|
|
190
|
+
if value is None:
|
|
191
|
+
click.echo(f"配置项 '{config_type}.{key}' 不存在")
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
click.echo(f"{config_type}.{key} = {value}")
|
|
195
|
+
|
|
196
|
+
except Exception as e:
|
|
197
|
+
logger.error(f"获取配置失败: {e}")
|
|
198
|
+
raise click.ClickException(f"获取配置失败: {e}")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@click.command()
|
|
202
|
+
@click.option('--type', 'config_type', default='main', help='配置类型:main(主配置) 或插件名称(如 quant, wiki)')
|
|
203
|
+
def types(config_type: str):
|
|
204
|
+
"""列出所有可用的配置类型"""
|
|
205
|
+
registry = get_registry()
|
|
206
|
+
plugin_names = registry.list_names()
|
|
207
|
+
|
|
208
|
+
click.echo("可用的配置类型:")
|
|
209
|
+
click.echo(" main - MOS 主配置 (log, llm, debug)")
|
|
210
|
+
for name in plugin_names:
|
|
211
|
+
click.echo(f" {name} - {name} 插件配置")
|
|
212
|
+
|
|
213
|
+
click.echo()
|
|
214
|
+
click.echo("示例:")
|
|
215
|
+
click.echo(" mos config list --type main")
|
|
216
|
+
click.echo(" mos config list --type quant")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
config.add_command(list)
|
|
220
|
+
config.add_command(set)
|
|
221
|
+
config.add_command(get)
|
|
222
|
+
config.add_command(types)
|
mos/cli/init.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import json
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from mos.core.config import get_config
|
|
6
|
+
from mos.core.logging import get_logger
|
|
7
|
+
from mos.core.plugin import get_registry
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_mos_dir():
|
|
13
|
+
"""获取 MOS 根目录"""
|
|
14
|
+
return Path.home() / ".mos"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _init_config_file(force=False):
|
|
18
|
+
"""初始化配置文件"""
|
|
19
|
+
config_file = _get_mos_dir() / "config.json"
|
|
20
|
+
|
|
21
|
+
if config_file.exists():
|
|
22
|
+
if force:
|
|
23
|
+
config_file.unlink()
|
|
24
|
+
click.echo(f" ⚠ 已删除旧配置文件: {config_file}")
|
|
25
|
+
else:
|
|
26
|
+
click.echo(f" [OK] 配置文件已存在: {config_file}")
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
config = get_config()
|
|
30
|
+
config_dict = config.model_dump()
|
|
31
|
+
|
|
32
|
+
config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
with open(config_file, 'w', encoding='utf-8') as f:
|
|
34
|
+
json.dump(config_dict, f, indent=2, ensure_ascii=False)
|
|
35
|
+
|
|
36
|
+
click.echo(f" [OK] 已创建配置文件: {config_file}")
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _init_log_dir():
|
|
41
|
+
"""初始化日志目录"""
|
|
42
|
+
log_dir = _get_mos_dir() / "logs"
|
|
43
|
+
|
|
44
|
+
if log_dir.exists():
|
|
45
|
+
click.echo(f" [OK] 日志目录已存在: {log_dir}")
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
click.echo(f" [OK] 已创建日志目录: {log_dir}")
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _init_cache_dir():
|
|
54
|
+
"""初始化缓存目录"""
|
|
55
|
+
cache_dir = _get_mos_dir() / "cache"
|
|
56
|
+
|
|
57
|
+
if cache_dir.exists():
|
|
58
|
+
click.echo(f" [OK] 缓存目录已存在: {cache_dir}")
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
click.echo(f" [OK] 已创建缓存目录: {cache_dir}")
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _init_plugin_dir():
|
|
67
|
+
"""初始化插件目录。
|
|
68
|
+
|
|
69
|
+
使用 ``Config.plugin.plugin_path`` 作为根目录;首次安装的
|
|
70
|
+
external 插件(通过 ``mos plugin install``)会被 ``git clone``
|
|
71
|
+
到该目录下。
|
|
72
|
+
"""
|
|
73
|
+
config = get_config()
|
|
74
|
+
plugin_dir = Path(config.plugin.get_expanded_path())
|
|
75
|
+
|
|
76
|
+
if plugin_dir.exists():
|
|
77
|
+
click.echo(f" [OK] 插件目录已存在: {plugin_dir}")
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
plugin_dir.mkdir(parents=True, exist_ok=True)
|
|
81
|
+
click.echo(f" [OK] 已创建插件目录: {plugin_dir}")
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _init_core(force=False):
|
|
86
|
+
"""初始化 MOS 核心框架。"""
|
|
87
|
+
click.echo()
|
|
88
|
+
click.echo("=" * 60)
|
|
89
|
+
click.echo("MOS 核心框架初始化")
|
|
90
|
+
click.echo("=" * 60)
|
|
91
|
+
click.echo()
|
|
92
|
+
|
|
93
|
+
mos_dir = _get_mos_dir()
|
|
94
|
+
click.echo(f"MOS 根目录: {mos_dir}")
|
|
95
|
+
click.echo()
|
|
96
|
+
|
|
97
|
+
if not mos_dir.exists():
|
|
98
|
+
click.echo("创建 MOS 根目录...")
|
|
99
|
+
mos_dir.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
click.echo(f" [OK] 已创建: {mos_dir}")
|
|
101
|
+
else:
|
|
102
|
+
click.echo(f" [OK] MOS 根目录已存在: {mos_dir}")
|
|
103
|
+
|
|
104
|
+
click.echo()
|
|
105
|
+
click.echo("初始化配置文件...")
|
|
106
|
+
_init_config_file(force)
|
|
107
|
+
|
|
108
|
+
click.echo()
|
|
109
|
+
click.echo("初始化日志目录...")
|
|
110
|
+
_init_log_dir()
|
|
111
|
+
|
|
112
|
+
click.echo()
|
|
113
|
+
click.echo("初始化缓存目录...")
|
|
114
|
+
_init_cache_dir()
|
|
115
|
+
|
|
116
|
+
click.echo()
|
|
117
|
+
click.echo("初始化插件目录...")
|
|
118
|
+
_init_plugin_dir()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _init_plugin(plugin_name: str, force: bool = False):
|
|
122
|
+
"""初始化单个插件。"""
|
|
123
|
+
registry = get_registry()
|
|
124
|
+
plugin = registry.get(plugin_name)
|
|
125
|
+
|
|
126
|
+
if plugin is None:
|
|
127
|
+
click.echo(f" [ERROR] 插件 '{plugin_name}' 未加载或未安装")
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
if plugin.init is None:
|
|
131
|
+
click.echo(f" [SKIP] 插件 '{plugin_name}' 没有初始化函数")
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
click.echo()
|
|
135
|
+
click.echo("=" * 60)
|
|
136
|
+
click.echo(f"初始化插件: {plugin_name}")
|
|
137
|
+
click.echo("=" * 60)
|
|
138
|
+
click.echo()
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
plugin.init(force)
|
|
142
|
+
click.echo()
|
|
143
|
+
click.echo(f"[OK] 插件 '{plugin_name}' 初始化完成")
|
|
144
|
+
return True
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.error(f"Plugin {plugin_name} init failed: {e}")
|
|
147
|
+
click.echo(f" [ERROR] 插件 '{plugin_name}' 初始化失败: {e}")
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _init_all_plugins(force: bool = False):
|
|
152
|
+
"""初始化所有已加载的插件。"""
|
|
153
|
+
registry = get_registry()
|
|
154
|
+
plugins = registry.all()
|
|
155
|
+
|
|
156
|
+
if not plugins:
|
|
157
|
+
click.echo(" [INFO] 没有已加载的插件")
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
click.echo()
|
|
161
|
+
click.echo("=" * 60)
|
|
162
|
+
click.echo("初始化所有插件")
|
|
163
|
+
click.echo("=" * 60)
|
|
164
|
+
click.echo()
|
|
165
|
+
|
|
166
|
+
initialized_count = 0
|
|
167
|
+
skipped_count = 0
|
|
168
|
+
|
|
169
|
+
for plugin in plugins:
|
|
170
|
+
if plugin.init is None:
|
|
171
|
+
click.echo(f" [SKIP] 插件 '{plugin.name}' 没有初始化函数")
|
|
172
|
+
skipped_count += 1
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
click.echo(f"初始化插件: {plugin.name}")
|
|
176
|
+
try:
|
|
177
|
+
plugin.init(force)
|
|
178
|
+
click.echo(f" [OK] 插件 '{plugin.name}' 初始化完成")
|
|
179
|
+
initialized_count += 1
|
|
180
|
+
except Exception as e:
|
|
181
|
+
logger.error(f"Plugin {plugin.name} init failed: {e}")
|
|
182
|
+
click.echo(f" [ERROR] 插件 '{plugin.name}' 初始化失败: {e}")
|
|
183
|
+
|
|
184
|
+
click.echo()
|
|
185
|
+
click.echo(f"初始化完成: {initialized_count} 个插件, {skipped_count} 个跳过")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@click.command()
|
|
189
|
+
@click.option('--force', '-f', is_flag=True, help='强制重新初始化(覆盖现有配置)')
|
|
190
|
+
@click.option('--plugin', '-p', 'plugin_name', help='初始化指定插件')
|
|
191
|
+
@click.option('--all', 'init_all', is_flag=True, help='初始化所有已加载的插件')
|
|
192
|
+
def init(force: bool, plugin_name: Optional[str], init_all: bool):
|
|
193
|
+
"""初始化 MOS 配置和环境
|
|
194
|
+
|
|
195
|
+
首次使用或重新配置时运行此命令。
|
|
196
|
+
将创建必要的配置文件和目录结构。
|
|
197
|
+
|
|
198
|
+
使用方式:
|
|
199
|
+
mos init # 初始化核心框架
|
|
200
|
+
mos init -p quant # 初始化 quant 插件
|
|
201
|
+
mos init --all # 初始化所有插件
|
|
202
|
+
mos init -f # 强制重新初始化
|
|
203
|
+
"""
|
|
204
|
+
# 如果指定了插件,只初始化插件
|
|
205
|
+
if plugin_name:
|
|
206
|
+
_init_plugin(plugin_name, force)
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
# 如果指定了 --all,初始化所有插件
|
|
210
|
+
if init_all:
|
|
211
|
+
_init_core(force)
|
|
212
|
+
_init_all_plugins(force)
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
# 默认只初始化核心框架
|
|
216
|
+
_init_core(force)
|
|
217
|
+
|
|
218
|
+
click.echo()
|
|
219
|
+
click.echo("=" * 60)
|
|
220
|
+
click.echo("[OK] 初始化完成!")
|
|
221
|
+
click.echo("=" * 60)
|
|
222
|
+
click.echo()
|
|
223
|
+
click.echo("接下来你可以:")
|
|
224
|
+
click.echo(" 1. 使用 'mos config list' 查看配置")
|
|
225
|
+
click.echo(" 2. 使用 'mos config set <key> <value>' 修改配置")
|
|
226
|
+
click.echo(" 3. 使用 'mos init -p <plugin>' 初始化插件")
|
|
227
|
+
click.echo()
|
|
228
|
+
click.echo("更多信息请参考项目文档")
|
|
229
|
+
click.echo()
|
mos/cli/mcp.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from mos.core.logging import get_logger
|
|
3
|
+
|
|
4
|
+
logger = get_logger(__name__)
|
|
5
|
+
|
|
6
|
+
@click.command()
|
|
7
|
+
def mcp():
|
|
8
|
+
"""
|
|
9
|
+
MCP命令
|
|
10
|
+
"""
|
|
11
|
+
from mos.core.mcp import mcp
|
|
12
|
+
logger.info("MCP server running...")
|
|
13
|
+
mcp.run(transport="streamable-http")
|
mos/cli/mos.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from mos.core.logging import setup_logging
|
|
4
|
+
from mos.core.config import get_config
|
|
5
|
+
from mos.core.plugin import load_entry_point_plugins
|
|
6
|
+
from mos.cli.config import config
|
|
7
|
+
from mos.cli.init import init
|
|
8
|
+
from mos.cli.plugin import plugin as plugin_cmd
|
|
9
|
+
from mos.cli.mcp import mcp
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
@click.version_option()
|
|
14
|
+
def cli():
|
|
15
|
+
setup_logging()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
cli.add_command(config)
|
|
19
|
+
cli.add_command(init)
|
|
20
|
+
cli.add_command(plugin_cmd)
|
|
21
|
+
cli.add_command(mcp)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
cfg = get_config()
|
|
25
|
+
disabled_plugins = list(cfg.plugin.disabled_plugins)
|
|
26
|
+
|
|
27
|
+
# Load all plugins via entry_points (pip-installed packages)
|
|
28
|
+
ep_results = load_entry_point_plugins(disabled_plugins)
|
|
29
|
+
for r in ep_results:
|
|
30
|
+
if r.status == "loaded" and r.definition is not None:
|
|
31
|
+
cmd = r.definition.command
|
|
32
|
+
if cmd.name and cmd.name not in cli.commands:
|
|
33
|
+
cli.add_command(cmd)
|
|
34
|
+
if r.name != cmd.name and r.name not in cli.commands:
|
|
35
|
+
cli.add_command(cmd, name=r.name)
|
|
36
|
+
# Register MCP tools if plugin supports it
|
|
37
|
+
if r.definition.register_mcp:
|
|
38
|
+
from mos.core.mcp import mcp
|
|
39
|
+
r.definition.register_mcp(mcp)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if __name__ == "__main__":
|
|
43
|
+
cli()
|