- 新增小红书前端模块(启动服务、内容分析、爆款创作、发布记录、技能卡片、拟人设置、AI 助手) - 新增 xhs-agent-desktop 独立桌面 Agent,对接 OMS 任务调度 - 登录页重构,整合微信登录与 OMS JWT 认证 - 订单、渠道、用户中心等页面与 API 增强 Co-authored-by: Cursor <cursoragent@cursor.com>
87 lines
2.4 KiB
Python
87 lines
2.4 KiB
Python
"""PyInstaller 单文件发布:解压 Chrome 扩展与配置到本地,引擎 exe 从内置 _MEIPASS 运行。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import shutil
|
||
import sys
|
||
import zipfile
|
||
from pathlib import Path
|
||
|
||
_RUNTIME_APP_NAME = "LikeAI"
|
||
_VERSION_FILE = "pack_version.txt"
|
||
_EXTENSION_ARCHIVE = "extension.zip"
|
||
|
||
|
||
def _local_runtime_root() -> Path:
|
||
base = os.environ.get("LOCALAPPDATA") or os.environ.get("APPDATA") or str(Path.home())
|
||
return Path(base) / _RUNTIME_APP_NAME
|
||
|
||
|
||
def _bundle_root() -> Path:
|
||
return Path(getattr(sys, "_MEIPASS", Path(__file__).resolve().parent.parent))
|
||
|
||
|
||
def _read_embedded_version() -> str:
|
||
path = _bundle_root() / _VERSION_FILE
|
||
if path.is_file():
|
||
return path.read_text(encoding="utf-8").strip()
|
||
return "0"
|
||
|
||
|
||
def bundled_engine_dir() -> Path:
|
||
"""单文件内置的 bridge / skills_cli(字节码,无 .py)。"""
|
||
return _bundle_root() / "engine"
|
||
|
||
|
||
def is_frozen_app() -> bool:
|
||
return bool(getattr(sys, "frozen", False)) and hasattr(sys, "_MEIPASS")
|
||
|
||
|
||
def bootstrap() -> Path:
|
||
"""首次运行解压扩展与配置;返回持久化数据目录(配置、.xhs_bridge)。"""
|
||
if not is_frozen_app():
|
||
return Path(__file__).resolve().parent.parent
|
||
|
||
root = _local_runtime_root()
|
||
root.mkdir(parents=True, exist_ok=True)
|
||
|
||
version = _read_embedded_version()
|
||
stamp = root / ".pack_version"
|
||
installed = stamp.read_text(encoding="utf-8").strip() if stamp.is_file() else ""
|
||
if installed != version:
|
||
_extract_extension(root)
|
||
_ensure_config_template(root)
|
||
stamp.write_text(version, encoding="utf-8")
|
||
|
||
return root
|
||
|
||
|
||
def _extract_extension(root: Path) -> None:
|
||
archive = _bundle_root() / _EXTENSION_ARCHIVE
|
||
if not archive.is_file():
|
||
return
|
||
ext_dir = root / "extension"
|
||
if ext_dir.exists():
|
||
shutil.rmtree(ext_dir, ignore_errors=True)
|
||
ext_dir.mkdir(parents=True, exist_ok=True)
|
||
with zipfile.ZipFile(archive, "r") as zf:
|
||
zf.extractall(ext_dir)
|
||
|
||
|
||
def _ensure_config_template(root: Path) -> None:
|
||
target = root / "config.yaml"
|
||
if target.is_file():
|
||
return
|
||
for name in ("config.example.yaml", "config.yaml"):
|
||
src = _bundle_root() / name
|
||
if src.is_file():
|
||
shutil.copy2(src, target)
|
||
return
|
||
|
||
|
||
def get_runtime_root() -> Path:
|
||
if is_frozen_app():
|
||
return bootstrap()
|
||
return Path(__file__).resolve().parent.parent
|