Files
MetaSkill/skills/le-app-design/scripts/log_call.py
T

141 lines
5.1 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.
#!/usr/bin/env python3
"""le-app-design 证据账本:所有对上游 Skill 的真实调用经此包裹,落入 calls.jsonl。
用法:
执行型调用(CLI 类上游):
python3 log_call.py <dep> --run-dir <RUN> -- python3 <DEP>/scripts/search.py "…" --design-system
装载型调用(文档型上游 / playbook):
python3 log_call.py <dep> --run-dir <RUN> --load <DEP>/SKILL.md
dep ∈ {ui-ux-pro-max, frontend-design, impeccable, le-app-design(自检)}
--run-dir 缺省时按序: $LE_APP_DESIGN_RUN_DIR > <cwd>/le-app-design-runs/<UTC 时间戳>-run/
行为:
- exec: 运行命令,透传 stdout/stderr,透传退出码;成功失败都记账。
- load: 读文件记 sha256 与字节数(证明用的是原文而非转述),文件缺失记 FAIL 退出 1。
- 记录字段: ts / dep / kind / command / exit_code / duration_ms / stdout_tail /
stderr_tail / bytes / sha256 / note。stdout/stderr 仅保留尾部 4000 字符防账本膨胀。
仅 python3 标准库。
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import subprocess
import sys
import time
from datetime import datetime, timezone
from pathlib import Path
TAIL = 4000
KNOWN_DEPS = {"ui-ux-pro-max", "frontend-design", "impeccable", "le-app-design"}
def default_run_dir() -> Path:
env = os.environ.get("LE_APP_DESIGN_RUN_DIR")
if env:
return Path(env).expanduser()
stamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%SZ")
return Path.cwd() / "le-app-design-runs" / f"{stamp}-run"
def append_record(run_dir: Path, record: dict) -> Path:
run_dir.mkdir(parents=True, exist_ok=True)
ledger = run_dir / "calls.jsonl"
with ledger.open("a", encoding="utf-8") as f:
f.write(json.dumps(record, ensure_ascii=False) + "\n")
return ledger
def confirm(dep: str, kind: str, status: str, ledger: Path, extra: str = "") -> None:
print(
f"[log_call] {dep} {kind} {status} → {ledger}"
+ (f" ({extra})" if extra else ""),
file=sys.stderr,
)
def main() -> int:
# 手工在第一个裸 "--" 处切分:argparse 的 REMAINDER 会把 -- 之前的选项误吞进命令
argv = sys.argv[1:]
cmd: list[str] = []
if "--" in argv:
split = argv.index("--")
argv, cmd = argv[:split], argv[split + 1:]
ap = argparse.ArgumentParser(description="le-app-design 调用账本")
ap.add_argument("dep", help="上游标识: ui-ux-pro-max | frontend-design | impeccable | le-app-design")
ap.add_argument("--run-dir", default=None, help="运行目录(缺省自动创建)")
ap.add_argument("--note", default="", help="阶段/用途备注,如 S2-design-system")
ap.add_argument("--load", metavar="PATH", default=None, help="装载型调用:记录读取的原文文件")
args = ap.parse_args(argv)
dep = args.dep
if dep not in KNOWN_DEPS:
print(f"[log_call] 未知依赖标识 {dep!r};允许: {sorted(KNOWN_DEPS)}", file=sys.stderr)
return 2
run_dir = Path(args.run_dir).expanduser() if args.run_dir else default_run_dir()
base = {"ts": datetime.now(timezone.utc).isoformat(timespec="seconds"), "dep": dep, "note": args.note}
if args.load:
path = Path(args.load).expanduser()
rec = dict(base, kind="load", command=f"load:{path}")
try:
data = path.read_bytes()
except OSError as e:
rec.update(exit_code=1, stderr_tail=f"read failed: {e}"[:TAIL])
ledger = append_record(run_dir, rec)
confirm(dep, "load", "FAIL", ledger, str(e))
return 1
rec.update(
exit_code=0,
bytes=len(data),
sha256=hashlib.sha256(data).hexdigest(),
)
ledger = append_record(run_dir, rec)
confirm(dep, "load", "OK", ledger, f"{len(data)} bytes sha256:{rec['sha256'][:12]}…")
return 0
if not cmd:
print("[log_call] 需要 --load PATH 或 -- <命令…>", file=sys.stderr)
return 2
rec = dict(base, kind="exec", command=json.dumps(cmd, ensure_ascii=False))
t0 = time.monotonic()
try:
proc = subprocess.run(
cmd, capture_output=True, text=True, timeout=900,
env=dict(os.environ, PYTHONDONTWRITEBYTECODE="1"),
)
rc, out, err = proc.returncode, proc.stdout or "", proc.stderr or ""
except subprocess.TimeoutExpired as e:
rc, out, err = 124, (e.stdout or b"").decode(errors="replace") if isinstance(e.stdout, bytes) else (e.stdout or ""), f"timeout after {e.timeout}s"
except FileNotFoundError as e:
rc, out, err = 127, "", f"command not found: {e}"
rec.update(
exit_code=rc,
duration_ms=int((time.monotonic() - t0) * 1000),
stdout_tail=out[-TAIL:],
stderr_tail=err[-TAIL:],
)
ledger = append_record(run_dir, rec)
if out:
sys.stdout.write(out)
if not out.endswith("\n"):
sys.stdout.write("\n")
if err:
sys.stderr.write(err)
if not err.endswith("\n"):
sys.stderr.write("\n")
confirm(dep, "exec", f"exit={rc}", ledger, f"{rec['duration_ms']}ms")
return rc
if __name__ == "__main__":
sys.exit(main())