Add OpenClaw skills, platform kit, and template docs

Made-with: Cursor
This commit is contained in:
2026-04-04 10:35:02 +08:00
parent e37b03c00f
commit 35f4758da2
83 changed files with 8971 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
"""
平台配置中心7 个 LLM 平台的静态配置、别名解析、以及通过 account-manager 暴露的 CLI 查询已登录网页账号。
"""
import json
import os
import subprocess
import sys
# ---------------------------------------------------------------------------
# 平台静态配置
# ---------------------------------------------------------------------------
LLM_PROVIDERS = {
"doubao": {
"label": "豆包",
"aliases": ["豆包"],
"web_url": "https://www.doubao.com/chat/",
"api_base": "https://ark.volces.com/api/v3",
"api_model": None, # 豆包需要用户在火山引擎控制台建推理接入点model=ep-xxx
"has_api": True,
"api_note": "需先在火山引擎控制台创建推理接入点,--model 传 endpoint_id格式 ep-xxx",
},
"deepseek": {
"label": "DeepSeek",
"aliases": ["深度求索"],
"web_url": "https://chat.deepseek.com",
"api_base": "https://api.deepseek.com/v1",
"api_model": "deepseek-chat",
"has_api": True,
"api_note": "模型可选deepseek-chat / deepseek-reasoner",
},
"qianwen": {
"label": "通义千问",
"aliases": ["通义", "千问", "qwen", "tongyi"],
"web_url": "https://tongyi.aliyun.com/qianwen/",
"api_base": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"api_model": "qwen-plus",
"has_api": True,
"api_note": "模型可选qwen-turbo / qwen-plus / qwen-max",
},
"kimi": {
"label": "Kimi",
"aliases": ["月之暗面", "moonshot"],
"web_url": "https://kimi.moonshot.cn",
"api_base": "https://api.moonshot.cn/v1",
"api_model": "moonshot-v1-8k",
"has_api": True,
"api_note": "模型可选moonshot-v1-8k / moonshot-v1-32k / moonshot-v1-128k",
},
"yiyan": {
"label": "文心一言",
"aliases": ["文心", "一言", "ernie", "wenxin"],
"web_url": "https://yiyan.baidu.com",
"api_base": "https://qianfan.baidubce.com/v2",
"api_model": "ernie-4.0-8k",
"has_api": True,
"api_note": "模型可选ernie-4.0-8k / ernie-3.5-8k",
},
"yuanbao": {
"label": "腾讯元宝",
"aliases": ["元宝"],
"web_url": "https://yuanbao.tencent.com/chat",
"api_base": None,
"api_model": None,
"has_api": False,
"api_note": "暂无公开 API仅支持网页模式",
},
"minimax": {
"label": "MiniMax",
"aliases": ["minimax", "MiniMax", "海螺", "海螺AI"],
"web_url": "https://chat.minimax.io/",
"api_base": "https://api.minimax.chat/v1",
"api_model": "MiniMax-Text-01",
"has_api": True,
"api_note": "模型可按 MiniMax 控制台可用模型调整,建议通过 --model 显式指定。",
},
}
# 构建别名查找表含中文、英文键、aliases
_ALIAS_TO_KEY: dict = {}
for _k, _spec in LLM_PROVIDERS.items():
_ALIAS_TO_KEY[_k] = _k
_ALIAS_TO_KEY[_k.lower()] = _k
_ALIAS_TO_KEY[_spec["label"]] = _k
for _a in (_spec.get("aliases") or []):
_ALIAS_TO_KEY[_a] = _k
_ALIAS_TO_KEY[_a.lower()] = _k
def resolve_provider_key(name: str):
"""将用户输入的平台名称/别名解析为内部 slug无法识别返回 None。"""
if not name:
return None
s = str(name).strip()
return _ALIAS_TO_KEY.get(s) or _ALIAS_TO_KEY.get(s.lower())
def provider_list_cn() -> str:
return "".join(s["label"] for s in LLM_PROVIDERS.values())
# ---------------------------------------------------------------------------
# 路径帮助(与 account-manager/scripts/main.py 完全一致:仅 JIANGCHANG_*
# ---------------------------------------------------------------------------
_OPENCLAW_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def get_data_root() -> str:
env = (os.getenv("JIANGCHANG_DATA_ROOT") or "").strip()
if env:
return env
if sys.platform == "win32":
return r"D:\jiangchang-data"
return os.path.join(os.path.expanduser("~"), ".jiangchang-data")
def get_user_id() -> str:
uid = (os.getenv("JIANGCHANG_USER_ID") or "").strip()
return uid or "_anon"
# ---------------------------------------------------------------------------
# 跨技能:仅通过 account-manager 提供的 CLI 读取账号(不直接打开其数据库文件)
# ---------------------------------------------------------------------------
def _account_manager_script_path() -> str:
return os.path.join(_OPENCLAW_DIR, "account-manager", "scripts", "main.py")
def find_logged_in_account(provider_key: str) -> dict | None:
"""
调用 account-managerpick-logged-in <platform_key>取该平台已登录login_status=1的优先账号。
成功返回与 account.py get 一致的 dict失败或不可用时返回 None。
"""
script = _account_manager_script_path()
if not os.path.isfile(script):
return None
try:
proc = subprocess.run(
[sys.executable, script, "pick-logged-in", provider_key],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
)
except OSError:
return None
raw = (proc.stdout or "").strip()
if not raw or raw.startswith("ERROR:"):
return None
try:
data = json.loads(raw.splitlines()[0])
except (json.JSONDecodeError, IndexError):
return None
if not isinstance(data, dict) or data.get("id") is None:
return None
plat = data.get("platform") or provider_key
if not (data.get("url") or "").strip():
data["url"] = LLM_PROVIDERS.get(provider_key, {}).get("web_url", "")
data["platform"] = plat
return data
# ---------------------------------------------------------------------------
# Chrome/Edge 检测(与 account-manager 逻辑保持一致)
# ---------------------------------------------------------------------------
def _win_find_exe(candidates):
for p in candidates:
if p and os.path.isfile(p):
return p
return None
def resolve_chromium_channel() -> str | None:
"""返回 'chrome' | 'msedge' | None。"""
if sys.platform != "win32":
return "chrome"
pf = os.environ.get("ProgramFiles", r"C:\Program Files")
pfx86 = os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)")
local = os.environ.get("LocalAppData", "")
chrome = _win_find_exe([
os.path.join(pf, "Google", "Chrome", "Application", "chrome.exe"),
os.path.join(pfx86, "Google", "Chrome", "Application", "chrome.exe"),
os.path.join(local, "Google", "Chrome", "Application", "chrome.exe") if local else "",
])
if chrome:
return "chrome"
edge = _win_find_exe([
os.path.join(pfx86, "Microsoft", "Edge", "Application", "msedge.exe"),
os.path.join(pf, "Microsoft", "Edge", "Application", "msedge.exe"),
os.path.join(local, "Microsoft", "Edge", "Application", "msedge.exe") if local else "",
])
if edge:
return "msedge"
return None