Files
skill-template/scripts/util/runtime_paths.py
chendelian 2e5a8d5eed
All checks were successful
技能自动化发布 / release (push) Successful in 5s
Add DATA_PATHS golden standard and resolve_data_path helpers.
Document skill-owned file layout under user data dir, extend runtime_paths with standard subdir helpers, add POLICY-DATA-PATH-001, and update CONFIG/RUNTIME cross-references.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-13 14:14:38 +08:00

146 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""数据根、技能目录、兄弟技能根路径与用户数据子目录解析。"""
from __future__ import annotations
import os
from jiangchang_skill_core.runtime_env import get_data_root, get_sibling_skills_root, get_user_id
from util.constants import SKILL_SLUG
_SCRIPTS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STANDARD_SUBDIR_DOWNLOADS = "downloads"
STANDARD_SUBDIR_IMPORTS = "imports"
STANDARD_SUBDIR_EXPORTS = "exports"
STANDARD_SUBDIR_UPLOADS = "uploads"
STANDARD_SUBDIR_CACHE = "cache"
STANDARD_SUBDIR_TEMP = "temp"
STANDARD_SUBDIR_RPA_ARTIFACTS = "rpa-artifacts"
STANDARD_SUBDIR_VIDEOS = "videos"
CONFIG_KEY_DOWNLOAD_DIR = "SKILL_DOWNLOAD_DIR"
CONFIG_KEY_IMPORT_DIR = "SKILL_IMPORT_DIR"
CONFIG_KEY_EXPORT_DIR = "SKILL_EXPORT_DIR"
CONFIG_KEY_UPLOAD_DIR = "SKILL_UPLOAD_DIR"
CONFIG_KEY_CACHE_DIR = "SKILL_CACHE_DIR"
CONFIG_KEY_TEMP_DIR = "SKILL_TEMP_DIR"
def get_skill_root() -> str:
return os.path.dirname(_SCRIPTS_DIR)
def get_openclaw_root() -> str:
return get_sibling_skills_root(_SCRIPTS_DIR)
def get_skills_root() -> str:
return get_sibling_skills_root(_SCRIPTS_DIR)
def get_skill_data_dir() -> str:
path = os.path.join(get_data_root(), get_user_id(), SKILL_SLUG)
os.makedirs(path, exist_ok=True)
return path
def get_db_path(filename: str | None = None) -> str:
name = filename or f"{SKILL_SLUG}.db"
return os.path.join(get_skill_data_dir(), name)
def _normalize_subdir(value: str) -> str:
cleaned = value.strip().strip("/\\")
if not cleaned or cleaned.startswith(".."):
raise ValueError(f"invalid skill data subdir: {value!r}")
return cleaned.replace("/", os.sep).replace("\\", os.sep)
def resolve_data_path(
config_key: str | None,
default_subdir: str,
*,
create: bool = True,
) -> str:
"""解析技能拥有路径:空配置 → 数据目录下 default_subdir相对 → 相对数据目录;绝对 → 原样。"""
from jiangchang_skill_core import config
base = get_skill_data_dir()
raw = (config.get(config_key) or "").strip() if config_key else ""
if not raw:
path = os.path.join(base, _normalize_subdir(default_subdir))
elif os.path.isabs(raw):
path = os.path.abspath(os.path.expanduser(raw))
else:
path = os.path.join(base, _normalize_subdir(raw))
if create:
os.makedirs(path, exist_ok=True)
return path
def resolve_input_path(raw_path: str, *, create_parent: bool = False) -> str:
"""解析 CLI 输入路径:绝对路径原样;相对路径相对 {skill_data_dir}/imports/。"""
text = (raw_path or "").strip()
if not text:
return text
expanded = os.path.expanduser(text)
if os.path.isabs(expanded):
return os.path.abspath(expanded)
imports_dir = get_imports_dir(create=create_parent)
return os.path.abspath(os.path.join(imports_dir, os.path.normpath(expanded)))
def get_downloads_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_DOWNLOAD_DIR, STANDARD_SUBDIR_DOWNLOADS, create=create)
def get_imports_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_IMPORT_DIR, STANDARD_SUBDIR_IMPORTS, create=create)
def get_exports_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_EXPORT_DIR, STANDARD_SUBDIR_EXPORTS, create=create)
def get_uploads_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_UPLOAD_DIR, STANDARD_SUBDIR_UPLOADS, create=create)
def get_cache_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_CACHE_DIR, STANDARD_SUBDIR_CACHE, create=create)
def get_temp_dir(*, create: bool = True) -> str:
return resolve_data_path(CONFIG_KEY_TEMP_DIR, STANDARD_SUBDIR_TEMP, create=create)
def get_rpa_artifacts_dir(batch_id: str, *, create: bool = True) -> str:
batch = (batch_id or "").strip() or "default"
path = os.path.join(get_skill_data_dir(), STANDARD_SUBDIR_RPA_ARTIFACTS, batch)
if create:
os.makedirs(path, exist_ok=True)
return path
def get_videos_dir(*, create: bool = True) -> str:
path = os.path.join(get_skill_data_dir(), STANDARD_SUBDIR_VIDEOS)
if create:
os.makedirs(path, exist_ok=True)
return path
def list_resolved_data_paths() -> dict[str, str]:
"""供 health / config-path 输出已解析的数据子目录(不创建目录)。"""
return {
"skill_data_dir": get_skill_data_dir(),
"downloads_dir": get_downloads_dir(create=False),
"imports_dir": get_imports_dir(create=False),
"exports_dir": get_exports_dir(create=False),
"uploads_dir": get_uploads_dir(create=False),
"cache_dir": get_cache_dir(create=False),
"temp_dir": get_temp_dir(create=False),
"videos_dir": get_videos_dir(create=False),
}