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:
@@ -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
|
||||
Reference in New Issue
Block a user