feat: sap-cli skill v2.1.0 — self-contained distributable package

- assets/: sap-cli source (v2.1.0, 26 commands, 16 object types)
- references/: tool constraints + error handling (self-contained)
- scripts/setup.py: one-click install/config/verify
- SKILL.md: full command reference + dual-platform install guide
- VERSION: 2.1.0

Built from D:/Codespace/sap-cli via scripts/pack_skill.py
This commit is contained in:
2026-06-13 20:52:24 +08:00
commit 1e39b6da88
49 changed files with 7172 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
from __future__ import annotations
import configparser
import os
from dataclasses import dataclass
from sapcli.exceptions import ConfigError
@dataclass(frozen=True)
class SAPConfig:
host: str
client: str
user: str
password: str
def __post_init__(self):
missing = []
if not self.host:
missing.append("host")
if not self.user:
missing.append("user")
if not self.password:
missing.append("password")
if missing:
raise ConfigError(
f"配置缺失: {', '.join(missing)}. "
f"请设置环境变量或配置文件 (config.ini)"
)
def list_profiles(config_path: str | None = None) -> list[str]:
"""列出配置文件中所有可用的 profile(section 名称)。"""
cfg = configparser.ConfigParser()
path = config_path or _find_config_file()
if path:
cfg.read(path, encoding="utf-8")
return cfg.sections()
def _find_config_file() -> str | None:
"""按搜索路径查找 config.ini。"""
search_paths: list[str] = []
script_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(script_dir)
search_paths.append(os.path.join(os.getcwd(), "config.ini"))
search_paths.append(os.path.join(project_root, "config.ini"))
for p in search_paths:
if os.path.isfile(p):
return p
return None
def load_config(
config_path: str | None = None,
profile: str | None = None,
) -> tuple[SAPConfig, str | None]:
cfg = configparser.ConfigParser()
search_paths: list[str] = []
if config_path:
search_paths.append(config_path)
script_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(script_dir)
search_paths.append(os.path.join(os.getcwd(), "config.ini"))
search_paths.append(os.path.join(project_root, "config.ini"))
loaded_from = None
for p in search_paths:
if os.path.isfile(p):
cfg.read(p, encoding="utf-8")
loaded_from = p
break
# profile 参数决定读哪个 section,默认 SAP
section = profile or "SAP"
sap = cfg[section] if cfg.has_section(section) else {}
host = os.environ.get("SAP_HOST", sap.get("host", ""))
client = os.environ.get("SAP_CLIENT", sap.get("client", "100"))
user = os.environ.get("SAP_USER", sap.get("user", ""))
config_password = sap.get("password", "")
env_password = os.environ.get("SAP_PASSWORD", "")
# 密码优先级: 环境变量 > keyring > config.ini
from sapcli.password import resolve_password
password = resolve_password(host, client, user, config_password, env_password)
return SAPConfig(
host=host,
client=client,
user=user,
password=password,
), loaded_from