CLI合并为oc.py/oc.bat菜单入口(数字+中文),mcpctl支持键值mcpcfg与on/off标记;删除6个分散bat
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
oc.py —— OC 工具箱统一入口(数字 / 中文菜单)
|
||||
|
||||
启动即打印菜单,输入编号或中文都能进:
|
||||
0 / 返回 / 退出 / q 退出
|
||||
1 / 技能 / 技能管理 / skill 进入技能管理(列表 / 推送 / 拉取 / 目录)
|
||||
2 / 连接器 / 连接器管理 / mcp 进入连接器管理(状态 / 开关 / 模式 / 备份)
|
||||
|
||||
也支持免菜单直传子命令(等价旧的分散入口):
|
||||
python oc.py skill list
|
||||
python oc.py mcp status
|
||||
python oc.py list (自动识别归属到技能管理)
|
||||
|
||||
底层实现复用同目录 ocskill.py / mcpctl.py,本文件只负责交互编排。
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 保证同目录模块可导入(无论从哪个工作目录启动)
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
import mcpctl # noqa: E402
|
||||
import ocskill # noqa: E402
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 控制台
|
||||
def fix_console() -> None:
|
||||
"""Windows 控制台中文输出兜底(UTF-8 重配)。"""
|
||||
for stream_name in ("stdout", "stderr"):
|
||||
stream = getattr(sys, stream_name, None)
|
||||
reconf = getattr(stream, "reconfigure", None)
|
||||
if callable(reconf):
|
||||
try:
|
||||
reconf(encoding="utf-8", errors="replace")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 输入解析
|
||||
def _norm(text: str) -> str:
|
||||
return (text or "").strip().lower().replace(" ", "")
|
||||
|
||||
|
||||
# 顶层别名:值 -> 传给对应函数的“行为键”
|
||||
TOP_ALIASES = {
|
||||
"0": "quit", "0.": "quit", "q": "quit", "quit": "quit", "exit": "quit",
|
||||
"退出": "quit", "返回": "quit", "回": "quit",
|
||||
"1": "skill", "1.": "skill", "skill": "skill", "skills": "skill",
|
||||
"技能": "skill", "技能管理": "skill", "管理技能": "skill", "技": "skill",
|
||||
"2": "mcp", "2.": "mcp", "mcp": "mcp", "连接器": "mcp",
|
||||
"连接器管理": "mcp", "管理连接器": "mcp", "连接": "mcp", "连": "mcp",
|
||||
"connector": "mcp", "connectors": "mcp",
|
||||
}
|
||||
|
||||
SKILL_ALIASES = {
|
||||
"0": "back", "0.": "back", "b": "back", "back": "back", "返回": "back", "回": "back",
|
||||
"1": "list", "1.": "list", "list": "list", "列表": "list", "查看": "list",
|
||||
"技能列表": "list", "看列表": "list",
|
||||
"2": "push", "2.": "push", "push": "push", "推送": "push", "上传": "push", "推": "push",
|
||||
"3": "pull", "3.": "pull", "pull": "pull", "拉取": "pull", "下载": "pull", "拉": "pull",
|
||||
"4": "dirs", "4.": "dirs", "dirs": "dirs", "目录": "dirs", "状态": "dirs",
|
||||
"技能目录": "dirs",
|
||||
}
|
||||
|
||||
MCP_ALIASES = {
|
||||
"0": "back", "0.": "back", "b": "back", "back": "back", "返回": "back", "回": "back",
|
||||
"1": "status", "1.": "status", "status": "status", "状态": "status", "查看": "status",
|
||||
"连接器状态": "status",
|
||||
"2": "on", "2.": "on", "on": "on", "开": "on", "开启": "on", "打开": "on",
|
||||
"3": "off", "3.": "off", "off": "off", "关": "off", "关闭": "off",
|
||||
"4": "mode", "4.": "mode", "mode": "mode", "模式": "mode",
|
||||
"5": "backup", "5.": "backup", "backup": "backup", "备份": "backup",
|
||||
}
|
||||
|
||||
|
||||
def ask(prompt: str) -> str:
|
||||
try:
|
||||
return input(prompt)
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print()
|
||||
return "0"
|
||||
|
||||
|
||||
def match(raw: str, aliases: dict, default: str | None = None) -> str | None:
|
||||
key = _norm(raw)
|
||||
if key in aliases:
|
||||
return aliases[key]
|
||||
# 中文模糊包含:输入“技能管理菜单”也能命中“技能管理”
|
||||
for k, v in aliases.items():
|
||||
if k and k in key and not k.isdigit():
|
||||
return v
|
||||
return default
|
||||
|
||||
|
||||
def pause(msg: str = "按回车返回…") -> None:
|
||||
try:
|
||||
input(msg)
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print()
|
||||
|
||||
|
||||
def banner(title: str) -> None:
|
||||
print()
|
||||
print("=" * 44)
|
||||
print(" " + title)
|
||||
print("=" * 44)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 子菜单:技能管理
|
||||
def menu_skill(shared: dict) -> None:
|
||||
while True:
|
||||
banner("技能管理(skill)")
|
||||
print(" 0. 返回上层")
|
||||
print(" 1. 查看技能列表")
|
||||
print(" 2. 推送技能到 OC(zip / 目录 / SKILL.md / base64)")
|
||||
print(" 3. 拉取技能到本机")
|
||||
print(" 4. 查看技能目录与连接状态")
|
||||
raw = ask("请选择(编号或中文): ")
|
||||
act = match(raw, SKILL_ALIASES)
|
||||
if act is None:
|
||||
print(" 没看懂「%s」,输入 0-4 或中文,如:列表 / 推送 / 拉取" % raw.strip())
|
||||
continue
|
||||
if act == "back":
|
||||
return
|
||||
|
||||
if act == "list":
|
||||
ocskill.main(["list"])
|
||||
elif act == "dirs":
|
||||
ocskill.main(["dirs"])
|
||||
elif act == "push":
|
||||
src = ask(" 技能路径(文件 / 目录 / zip,回车取消): ").strip()
|
||||
if not src:
|
||||
continue
|
||||
argv = ["push", src]
|
||||
if match(ask(" 同名覆盖?(y/N): "), {"y": "y", "yes": "y", "是": "y"}, None) == "y":
|
||||
argv.append("--force")
|
||||
if match(ask(" 只校验不写入?(y/N): "), {"y": "y", "yes": "y", "是": "y"}, None) == "y":
|
||||
argv.append("--dry-run")
|
||||
ocskill.main(argv)
|
||||
elif act == "pull":
|
||||
name = ask(" 要拉取的技能名(回车取消): ").strip()
|
||||
if not name:
|
||||
continue
|
||||
argv = ["pull", name]
|
||||
out = ask(" 输出目录(回车=当前目录): ").strip()
|
||||
if out:
|
||||
argv += ["--out", out]
|
||||
if match(ask(" 覆盖本地同名目录?(y/N): "), {"y": "y", "yes": "y", "是": "y"}, None) == "y":
|
||||
argv.append("--force")
|
||||
ocskill.main(argv)
|
||||
pause()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 子菜单:连接器管理
|
||||
def menu_mcp(shared: dict) -> None:
|
||||
# 目标(本机 / WSL)本次会话记住,子菜单里随时可换
|
||||
target = shared.get("target", "local")
|
||||
while True:
|
||||
banner("连接器管理(MCP,wb 语境的“连接器”)")
|
||||
print(" 当前目标: %s" % ("本机" if target == "local" else "WSL"))
|
||||
print(" 0. 返回上层")
|
||||
print(" 1. 查看状态(模式 + 开关)")
|
||||
print(" 2. 开启连接器(对调为含 MCP 配置 + 热重载)")
|
||||
print(" 3. 关闭连接器(对调为安静配置 + 热重载)")
|
||||
print(" 4. 查看 / 修改模式(自动 / 手动 / 关闭)")
|
||||
print(" 5. 备份当前 mcpcfg.txt")
|
||||
print(" t. 切换目标(本机 <-> WSL)")
|
||||
raw = ask("请选择(编号或中文): ")
|
||||
key = _norm(raw)
|
||||
if key in ("t", "切换", "target", "目标"):
|
||||
target = "wsl" if target == "local" else "local"
|
||||
shared["target"] = target
|
||||
print(" 已切换目标 -> %s" % ("本机" if target == "local" else "WSL"))
|
||||
continue
|
||||
act = match(raw, MCP_ALIASES)
|
||||
if act is None:
|
||||
print(" 没看懂「%s」,输入 0-5 / t 或中文,如:状态 / 开启 / 关闭 / 模式" % raw.strip())
|
||||
continue
|
||||
if act == "back":
|
||||
return
|
||||
|
||||
base = ["--target", target]
|
||||
if act == "status":
|
||||
mcpctl.main(base + ["status"])
|
||||
elif act == "on":
|
||||
mcpctl.main(base + ["on"])
|
||||
elif act == "off":
|
||||
mcpctl.main(base + ["off"])
|
||||
elif act == "backup":
|
||||
mcpctl.main(base + ["backup"])
|
||||
elif act == "mode":
|
||||
val = ask(" 新模式(自动 / 手动 / 关闭,回车只查看): ").strip()
|
||||
mcpctl.main(base + ["mode"] + ([val] if val else []))
|
||||
pause()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 顶层菜单
|
||||
def menu_top() -> None:
|
||||
shared: dict = {"target": "local"}
|
||||
while True:
|
||||
banner("OC 工具箱(输入编号或中文,0 退出)")
|
||||
print(" 0. 退出")
|
||||
print(" 1. 技能管理(skill 拉取 / 推送)")
|
||||
print(" 2. 连接器管理(MCP 开关 / 模式)")
|
||||
raw = ask("请选择: ")
|
||||
act = match(raw, TOP_ALIASES)
|
||||
if act is None:
|
||||
print(" 没看懂「%s」,输入 0 / 1 / 2,或:退出 / 技能管理 / 连接器管理" % raw.strip())
|
||||
continue
|
||||
if act == "quit":
|
||||
print("再见。")
|
||||
return
|
||||
if act == "skill":
|
||||
menu_skill(shared)
|
||||
elif act == "mcp":
|
||||
menu_mcp(shared)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 直传模式
|
||||
SKILL_CMDS = {"list", "push", "pull", "dirs"}
|
||||
MCP_CMDS = {"status", "on", "off", "mode", "backup"}
|
||||
|
||||
|
||||
def dispatch(argv: list[str]) -> int:
|
||||
"""有参数时免菜单直传(兼容旧分散入口的用法)。"""
|
||||
head = _norm(argv[0]) if argv else ""
|
||||
rest = argv[1:]
|
||||
if head in ("skill", "skills", "技能", "技能管理"):
|
||||
return ocskill.main(rest)
|
||||
if head in ("mcp", "连接器", "连接器管理"):
|
||||
return mcpctl.main(rest)
|
||||
if head in SKILL_CMDS:
|
||||
return ocskill.main(argv)
|
||||
if head in MCP_CMDS:
|
||||
return mcpctl.main(argv)
|
||||
print("未知命令: %s(技能: %s;连接器: %s)" % (
|
||||
argv[0], "/".join(sorted(SKILL_CMDS)), "/".join(sorted(MCP_CMDS))))
|
||||
return 2
|
||||
|
||||
|
||||
def main() -> int:
|
||||
fix_console()
|
||||
if len(sys.argv) > 1:
|
||||
return dispatch(sys.argv[1:])
|
||||
menu_top()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user