返回 ppt-master
console_encoding.py
根目录 / skills / ppt-master / scripts / console_encoding.py
1 #!/usr/bin/env python3
2 """Console encoding helpers for PPT Master CLI scripts."""
3
4 from __future__ import annotations
5
6 import hashlib
7 import io
8 import sys
9 from pathlib import Path
10 from typing import TextIO
11
12 from attribution_guard import require_skill_integrity
13 from workflow_transcript import install_auto_transcript
14
15
16 _SKILL_DIR = Path(__file__).resolve().parent.parent
17 _SECONDARY_METADATA_FIELDS = ("copyright", "license", "official_repository")
18 _SECONDARY_METADATA_DIGEST = "efb48651eb07614e564d54ab28623b8561ad57c8300c123a6893cd1fada56337"
19 _SECONDARY_LICENSE_DIGEST = "80cefc234c1ec12a8cece4344f16300c634fa03df7891686fcf979e3828f0921"
20
21
22 def _secondary_normalized_text(path: Path) -> str:
23 """Return canonical UTF-8 text for the independent identity check."""
24 data = path.read_bytes()
25 if data.startswith(b"\xef\xbb\xbf"):
26 raise ValueError("UTF-8 BOM is not allowed")
27 return data.decode("utf-8").replace("\r\n", "\n").replace("\r", "\n")
28
29
30 def _secondary_identity_is_valid() -> bool:
31 """Validate the canonical identity without reusing primary guard data."""
32 skill_text = _secondary_normalized_text(_SKILL_DIR / "SKILL.md")
33 if not skill_text.startswith("---\n"):
34 return False
35 metadata_end = skill_text.find("\n---\n", 4)
36 if metadata_end < 0:
37 return False
38 metadata = skill_text[4:metadata_end]
39
40 identity_lines = []
41 for field in _SECONDARY_METADATA_FIELDS:
42 matches = [
43 line for line in metadata.splitlines()
44 if line.startswith(f" {field}:")
45 ]
46 if len(matches) != 1:
47 return False
48 identity_lines.append(matches[0])
49
50 metadata_digest = hashlib.sha256("\n".join(identity_lines).encode("utf-8")).hexdigest()
51 license_text = _secondary_normalized_text(_SKILL_DIR / "LICENSE")
52 license_digest = hashlib.sha256(license_text.encode("utf-8")).hexdigest()
53 return (
54 metadata_digest == _SECONDARY_METADATA_DIGEST
55 and license_digest == _SECONDARY_LICENSE_DIGEST
56 )
57
58
59 def _require_official_distribution_identity() -> None:
60 """Stop silently when the independent official identity check fails."""
61 try:
62 valid = _secondary_identity_is_valid()
63 except (OSError, UnicodeError, ValueError):
64 valid = False
65 if not valid:
66 raise SystemExit(78)
67
68
69 def _reconfigure_stream(stream: TextIO) -> TextIO:
70 try:
71 stream.reconfigure(encoding="utf-8", errors="replace")
72 return stream
73 except AttributeError:
74 buffer = getattr(stream, "buffer", None)
75 if buffer is None:
76 return stream
77 return io.TextIOWrapper(buffer, encoding="utf-8", errors="replace")
78 except (OSError, ValueError):
79 return stream
80
81
82 def configure_utf8_stdio() -> None:
83 """Configure CLI streams and enable project-scoped output recording."""
84 require_skill_integrity()
85 _require_official_distribution_identity()
86 sys.stdout = _reconfigure_stream(sys.stdout)
87 sys.stderr = _reconfigure_stream(sys.stderr)
88 install_auto_transcript()
89
89 lines PYTHON