User edition: prune docs, license gate, obfuscate core lib.

Keep deploy/basic docs only; integrate sq.bz121.com license client; encrypt strategy/trade/key_monitor/options/hedge_plan for release.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-17 16:30:58 +08:00
parent 53863559f4
commit 16ef0f31e3
154 changed files with 1998 additions and 27914 deletions
+206
View File
@@ -0,0 +1,206 @@
"""核心包加密发布脚本。
优先 PyArmor;本机装不上时使用内置 marshal 加密后端(去可读源码)。
用法(仓库根):
python scripts/obfuscate_release.py --dry-run
python scripts/obfuscate_release.py --backend marshal --output dist/obfuscated_lib
python scripts/obfuscate_release.py --backend marshal --apply
不加密: lib/license/、模板 templates/、非目标包。
"""
from __future__ import annotations
import argparse
import base64
import compileall
import hashlib
import marshal
import py_compile
import random
import shutil
import struct
import subprocess
import sys
import zlib
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
OBFUSCATE_PACKAGES = [
"strategy",
"trade",
"key_monitor",
"options",
"hedge_plan",
]
_LOADER_TEMPLATE = '''# obfuscated module — do not edit
import base64, marshal, zlib
_B = {blob!r}
_K = {key!r}
def _d(b, k):
raw = zlib.decompress(base64.b64decode(b))
key = k.encode("utf-8") if isinstance(k, str) else k
out = bytearray(len(raw))
for i, c in enumerate(raw):
out[i] = c ^ key[i % len(key)]
return bytes(out)
exec(marshal.loads(_d(_B, _K)), globals())
'''
def _xor_compress(data: bytes, key: bytes) -> str:
out = bytearray(len(data))
for i, c in enumerate(data):
out[i] = c ^ key[i % len(key)]
return base64.b64encode(zlib.compress(bytes(out), 9)).decode("ascii")
def _obfuscate_py_file(src: Path, dest: Path, key: str) -> None:
source = src.read_text(encoding="utf-8")
code = compile(source, str(src), "exec", dont_inherit=True)
blob = _xor_compress(marshal.dumps(code), key.encode("utf-8"))
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(_LOADER_TEMPLATE.format(blob=blob, key=key), encoding="utf-8")
def _copy_non_py(src_dir: Path, dest_dir: Path) -> None:
for path in src_dir.rglob("*"):
if path.is_dir():
continue
if path.suffix == ".py":
continue
rel = path.relative_to(src_dir)
target = dest_dir / rel
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(path, target)
def marshal_obfuscate_package(src_pkg: Path, dest_pkg: Path, key: str) -> int:
if dest_pkg.exists():
shutil.rmtree(dest_pkg)
dest_pkg.mkdir(parents=True, exist_ok=True)
count = 0
for path in src_pkg.rglob("*.py"):
rel = path.relative_to(src_pkg)
# keep __init__.py structure but still obfuscate
_obfuscate_py_file(path, dest_pkg / rel, key)
count += 1
_copy_non_py(src_pkg, dest_pkg)
return count
def _run(cmd: list[str], cwd: Path) -> None:
print("+", " ".join(cmd))
subprocess.check_call(cmd, cwd=str(cwd))
def try_pyarmor(packages: list[str], out: Path) -> bool:
try:
import pyarmor # noqa: F401
except ImportError:
return False
if out.exists():
shutil.rmtree(out)
out.mkdir(parents=True, exist_ok=True)
src_dirs = [str(REPO_ROOT / "lib" / n) for n in packages]
for cmd in (
[sys.executable, "-m", "pyarmor.cli", "gen", "-O", str(out), *src_dirs],
["pyarmor", "gen", "-O", str(out), *src_dirs],
):
try:
_run(cmd, REPO_ROOT)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
continue
return False
def main() -> int:
parser = argparse.ArgumentParser(description="Obfuscate core lib packages")
parser.add_argument("--output", default="dist/obfuscated_lib")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--apply", action="store_true", help="覆盖 lib/ 目标包(先备份到 dist/lib_backup_plain")
parser.add_argument(
"--backend",
choices=("auto", "pyarmor", "marshal"),
default="auto",
help="auto=有 pyarmor 用 pyarmor,否则 marshal",
)
args = parser.parse_args()
lib = REPO_ROOT / "lib"
out = REPO_ROOT / args.output
packages = [n for n in OBFUSCATE_PACKAGES if (lib / n).is_dir()]
print("packages:", ", ".join(packages))
if args.dry_run:
return 0
backend = args.backend
if backend == "auto":
backend = "pyarmor"
try:
import pyarmor # noqa: F401
except ImportError:
backend = "marshal"
print("pyarmor 未安装,使用 marshal 后端")
key = hashlib.sha256(b"crypto_monitor_user_release_v1").hexdigest()[:32]
if backend == "pyarmor":
ok = try_pyarmor(packages, out)
if not ok:
print("pyarmor 失败,回退 marshal", file=sys.stderr)
backend = "marshal"
if backend == "marshal":
if out.exists():
shutil.rmtree(out)
out.mkdir(parents=True, exist_ok=True)
total = 0
for name in packages:
n = marshal_obfuscate_package(lib / name, out / name, key)
print(f"marshal {name}: {n} py files")
total += n
print(f"encrypted output: {out} ({total} files)")
# smoke import from output path
sys.path.insert(0, str(out.parent))
# packages live in out/<name>, need lib-like layout for import?
# For smoke: add out to path and import strategy if it has __init__
smoke_root = out
sys.path.insert(0, str(smoke_root))
try:
import importlib
# packages are top-level under out/
m = importlib.import_module("trade.trade_policy_lib")
print("smoke import trade.trade_policy_lib:", getattr(m, "__file__", m))
except Exception as e:
print("smoke import note:", e)
if args.apply:
for name in packages:
candidates = [out / name, out / "lib" / name]
src = next((c for c in candidates if c.exists()), None)
if src is None:
print(f"警告: 未找到 {name}")
continue
dest = lib / name
backup = REPO_ROOT / "dist" / "lib_backup_plain" / name
backup.parent.mkdir(parents=True, exist_ok=True)
if dest.exists():
if backup.exists():
shutil.rmtree(backup)
shutil.copytree(dest, backup)
shutil.rmtree(dest)
shutil.copytree(src, dest)
print(f"applied: lib/{name}")
print("完成。请验证启动后再 push。")
return 0
if __name__ == "__main__":
raise SystemExit(main())