feat(docx): 跨平台启动器替换 bash 包装,复用预装依赖免每次安装 (#21)

## 概述 / Summary

把 docx 技能对"客户端预装运行时依赖"的复用方式从 **bash 包装脚本**改为**跨平台 runtime 启动器**,实现
Win/macOS/Linux 一致、不依赖 Git Bash,并修复若干 POSIX 硬编码导致的 Windows 崩溃点。

Switch the docx skill's reuse of client-preinstalled runtime deps from a
**bash wrapper** to **cross-platform runtime launchers**, so it behaves
identically on Win/macOS/Linux without Git Bash, and fix several
POSIX-hardcoded crashes on Windows.

## 改动 / Changes

- **新增 / Add** `scripts/preload-deps.cjs`(Node 预加载,注入 `NODE_PATH`)与
`scripts/with-deps.py`(Python 启动器,按需切换到内置含 lxml 的 Python);**删除** bash 版
`with-deps.sh`。
- 生成走 `node -r preload-deps.cjs`,office 脚本走 `python with-deps.py` ——
离线复用预装的 docx-js / defusedxml / lxml,免每次 `npm`/`pip install`,且**不依赖
bash**。
- `comment.py` 补 defusedxml sys.path shim;`validate.py` 修临时目录泄漏(atexit
清理)。
- `accept_changes.py` 去除 `/tmp` 硬编码(`tempfile.gettempdir` +
`Path.as_uri`);`soffice.py` 仅 Linux 启用 AF_UNIX shim,避免 Windows 崩溃。
- `SKILL.md` / `SKILL.zh-CN.md` 同步命令形式、加 ESM
警告与外部工具(pandoc/LibreOffice/poppler)跨平台安装指引,`source_hash` 重算。

## 测试 / Testing

- 真实 dev 根目录端到端:生成 docx(免安装)+ 完整 XSD 校验(含 lxml)+ unpack/pack 往返均通过。
- 仓库 `validate-i18n.py` 校验通过;全 py 脚本 `py_compile` + `preload-deps.cjs`
`node --check` 通过。

---

- [x] 我已阅读并同意 CLA / I have read and agree to the CLA

Co-authored-by: 张馨元 <zhangxy@iynss.com>
Co-authored-by: Yige <a@wyr.me>
This commit is contained in:
Zxy-y
2026-06-04 11:14:36 +08:00
committed by GitHub
parent b15fce19bf
commit 17fe79ab49
10 changed files with 276 additions and 77 deletions

View File

@@ -0,0 +1,23 @@
// preload-deps.cjs —— 跨平台 Node 预加载(无需 bash让 docx 生成复用客户端预装依赖
//
// 客户端启动时会把 docx-js 预装到 <DESIRECORE_ROOT>/runtime-deps/node_modules/。
// 本文件通过 `node -r` 预加载,把该目录注入模块解析路径,使生成脚本里的
// require('docx') 无需联网 `npm install` 即可命中预装库。纯 Node 实现,在
// Windows / macOS / Linux 上用同一条命令运行,不依赖 bash / Git Bash
//
// node -r "<skill-dir>/scripts/preload-deps.cjs" generate.js
//
// 若预装目录不存在(老客户端 / 离线种子缺失),则不做任何事,由生成脚本自身
// 回退require 失败 → 提示 npm install -g docx。env 仅作用于本进程。
'use strict'
const path = require('path')
const fs = require('fs')
const Module = require('module')
// scripts → docx → skills → <ROOT>;预装 Node 依赖在 <ROOT>/runtime-deps/node_modules
const depsDir = path.resolve(__dirname, '..', '..', '..', 'runtime-deps', 'node_modules')
if (fs.existsSync(depsDir)) {
// path.delimiter 跨平台自动取 ';'(Windows) / ':'(POSIX)
process.env.NODE_PATH = depsDir + (process.env.NODE_PATH ? path.delimiter + process.env.NODE_PATH : '')
Module._initPaths() // 让随后运行的 generate.js 的 require('docx') 命中预装库
}