arxiv-paper-zh 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -241,6 +241,8 @@ arxiv-paper/EST/
241
241
  - `paper-en/<paper-name>-en.pdf`:英文原版编译结果。
242
242
  - `paper-zh/<paper-name>-zh.pdf`:中文译版编译结果。
243
243
 
244
+ 原始下载物只允许保存为 `latex/source.tar`,不另建根级 `source/`、`source.tar` 或 `latex/source/` 中转路径。Agent 自建的渲染图、截图和诊断文件统一写入论文根目录的 `tmp/`;完整交付校验通过后自动删除该目录。
245
+
244
246
  `paper-name` 使用用户熟悉的简短名称并保留大小写,例如 `EST`、`Onetrans`,且只能包含英文字母、数字、点、下划线和连字符。
245
247
 
246
248
  整个参考文献部分保持原文,包括标题和全部条目;`.bib`、`.bbl`、内嵌 bibliography 环境和单独的参考文献 TeX 文件均不参与翻译。正文中的文献综述仍照常翻译。原图内部文字不修改,只翻译必要 caption。公式内的英文说明按“公式不变”原则保留。
@@ -266,6 +268,9 @@ python3 skills/arxiv-paper-zh/scripts/audit_tex_translation.py arxiv-paper/EST/l
266
268
 
267
269
  # 自动运行 XeLaTeX 与 BibTeX/Biber,检查构建日志
268
270
  python3 skills/arxiv-paper-zh/scripts/build_and_check.py arxiv-paper/EST/latex/paper-zh/main.tex --tex-bin /path/to/tex/bin
271
+
272
+ # 校验完整交付物,并在成功后删除论文根目录的 tmp/
273
+ python3 skills/arxiv-paper-zh/scripts/finalize_output.py arxiv-paper/EST
269
274
  ```
270
275
 
271
276
  ## 兼容性说明
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arxiv-paper-zh",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Install the arxiv-paper-zh Agent Skill for Codex, Claude Code, and compatible agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,12 +27,14 @@ arxiv-paper/<paper-name>/
27
27
  python3 scripts/prepare_output_layout.py <paper-name> --root arxiv-paper
28
28
  ```
29
29
 
30
- 以脚本输出的绝对路径为准,不另建平行交付目录。`.translation-tasks/`、渲染图和日志属于论文目录内的临时文件,不得混入最终 PDF 目录。
30
+ 以脚本输出的绝对路径为准,不另建平行交付目录。原始下载物的唯一允许路径是 `latex/source.tar`;不得另建论文根目录下的 `source/`、`source.tar` 或 `latex/source/` 作为下载、解压中转。若 arXiv 源包自身包含 `source/` 子目录,可原样保留在 `latex/paper-en/` 内。
31
+
32
+ Agent 自建的下载中转、页面渲染、截图和诊断临时文件全部放在论文根目录的 `tmp/`。失败时保留它用于诊断;只有全部交付物通过最终校验后才删除。`.translation-tasks/` 位于中文源码目录,不得混入最终 PDF 目录。
31
33
 
32
34
  ## 工作流
33
35
 
34
36
  1. 从 arXiv 摘要页或 API 核验规范化 ID、完整标题、作者、摘要、版本和日期。简称不是唯一标识;候选不唯一时让用户确认。向用户明确标题、作者和 arXiv ID。
35
- 2. 创建输出目录,将 `https://export.arxiv.org/e-print/<ID>` 保存为 `latex/source.tar` 并解压到 `latex/paper-en/`。用主 TeX 的标题、作者或 README 二次核验身份;不一致时停止。
37
+ 2. 创建输出目录,将 `https://export.arxiv.org/e-print/<ID>` 直接保存为脚本返回的 `latex/source.tar`;下载失败时删除不完整文件。直接解压到 `latex/paper-en/`,不得创建额外的 `source/` 中转目录。用主 TeX 的标题、作者或 README 二次核验身份;不一致时停止。
36
38
  3. 将英文源码内容完整复制到 `latex/paper-zh/`,不得多套一层目录;此后只修改中文副本。分别定位中英文入口文件。
37
39
  4. 先完成中文入口的 XeLaTeX/ctex 改造和模板可见字符串本地化,再生成翻译任务。通常加入 `\usepackage[UTF8,fontset=fandol]{ctex}`,移除仅适用于 pdfLaTeX 的 `inputenc` 和 T1 `fontenc`。生成任务后、合并任务前不要再编辑中文 TeX 源码,合并器会检查快照。
38
40
  5. 生成紧凑任务包。脚本包含入口文件,因此单文件论文也能按片段并行;它自动省略参考文献,并用可逆占位符保护公式、引用、URL、代码和注释:
@@ -68,8 +70,8 @@ python3 scripts/prepare_output_layout.py <paper-name> --root arxiv-paper
68
70
  arxiv-paper/<paper-name>/latex/paper-zh/main.tex --tex-bin /path/to/tex/bin
69
71
  ```
70
72
 
71
- 9. 分别低分辨率渲染中英文 PDF 的全部页面并检查裁切、重叠、溢出、图片和页数;只对可疑页面高分辨率渲染。另用支持 CJK 的系统 PDF 引擎抽查中文字体。Poppler 缺少 CMap 时不得把空白中文误判为正常。
72
- 10. 将英文成品复制为 `paper-en/<paper-name>-en.pdf`,中文成品复制为 `paper-zh/<paper-name>-zh.pdf`。回复列出论文身份、两套源码目录和两个 PDF 的绝对路径。
73
+ 9. 将中英文页面渲染到 `tmp/render-en/` 和 `tmp/render-zh/`,分别低分辨率检查全部页面的裁切、重叠、溢出、图片和页数;只对可疑页面高分辨率渲染。另用支持 CJK 的系统 PDF 引擎抽查中文字体。Poppler 缺少 CMap 时不得把空白中文误判为正常。
74
+ 10. 将英文成品复制为 `paper-en/<paper-name>-en.pdf`,中文成品复制为 `paper-zh/<paper-name>-zh.pdf`。最后运行 `python3 scripts/finalize_output.py arxiv-paper/<paper-name>`;它确认 `latex/source.tar`、两套源码和两个 PDF 均非空,拒绝额外的源码中转路径,并仅在校验成功后删除 `tmp/`。成功后再回复论文身份、两套源码目录和两个 PDF 的绝对路径。
73
75
 
74
76
  ## 翻译边界
75
77
 
@@ -94,8 +96,10 @@ python3 scripts/prepare_tex_runtime.py --preset \
94
96
  ## 完成标准
95
97
 
96
98
  - arXiv 元数据与源码身份两次核验通过,`latex/paper-en/` 未修改。
99
+ - 原始下载包仅存在于非空的 `latex/source.tar`,没有 Agent 创建的 `source/` 或其他源码中转副本。
97
100
  - 中文任务全部合并,全局漏译审计已人工复核;所有可见自然语言均已翻译或明确允许保留。
98
101
  - 中英文 PDF 均构建成功,参考文献和交叉引用收敛,中文版无缺字。
99
102
  - 中英文 PDF 全部页面均已检查,中文字体经 CJK 能力正常的渲染器确认。
103
+ - `finalize_output.py` 校验成功,论文根目录的 `tmp/` 已移除。
100
104
 
101
105
  方案参考科学空间文章[《让 AI 翻译一篇完整的论文》](https://spaces.ac.cn/archives/11578),并结合紧凑任务包、并行 agent、依赖缓存和自动审计实现。
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env python3
2
+ """Validate one paper's deliverables and remove its managed tmp directory."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ import re
9
+ import shutil
10
+ from pathlib import Path
11
+
12
+
13
+ SAFE_NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
14
+
15
+
16
+ def has_files(path: Path) -> bool:
17
+ return path.is_dir() and any(item.is_file() for item in path.rglob("*"))
18
+
19
+
20
+ def remove_path(path: Path) -> bool:
21
+ if path.is_symlink() or path.is_file():
22
+ path.unlink()
23
+ return True
24
+ if path.is_dir():
25
+ shutil.rmtree(path)
26
+ return True
27
+ return False
28
+
29
+
30
+ def main() -> int:
31
+ parser = argparse.ArgumentParser()
32
+ parser.add_argument("paper_root", type=Path)
33
+ args = parser.parse_args()
34
+
35
+ paper_root = args.paper_root.resolve()
36
+ name = paper_root.name
37
+ if not paper_root.is_dir():
38
+ parser.error(f"paper root does not exist: {paper_root}")
39
+ if not SAFE_NAME.fullmatch(name):
40
+ parser.error("paper directory name must match [A-Za-z0-9][A-Za-z0-9._-]*")
41
+
42
+ source_archive = paper_root / "latex" / "source.tar"
43
+ pdf_en = paper_root / "paper-en" / f"{name}-en.pdf"
44
+ pdf_zh = paper_root / "paper-zh" / f"{name}-zh.pdf"
45
+ required_files = {
46
+ "source archive": source_archive,
47
+ "English PDF": pdf_en,
48
+ "Chinese PDF": pdf_zh,
49
+ }
50
+ required_sources = {
51
+ "English source": paper_root / "latex" / "paper-en",
52
+ "Chinese source": paper_root / "latex" / "paper-zh",
53
+ }
54
+ forbidden_staging = (
55
+ paper_root / "source",
56
+ paper_root / "source.tar",
57
+ paper_root / "latex" / "source",
58
+ )
59
+
60
+ errors = [
61
+ f"missing or empty {label}: {path}"
62
+ for label, path in required_files.items()
63
+ if not path.is_file() or not path.stat().st_size
64
+ ]
65
+ errors.extend(
66
+ f"missing or empty {label} directory: {path}"
67
+ for label, path in required_sources.items()
68
+ if not has_files(path)
69
+ )
70
+ errors.extend(
71
+ f"unexpected source staging path: {path}; use {source_archive}"
72
+ for path in forbidden_staging
73
+ if path.exists() or path.is_symlink()
74
+ )
75
+ if errors:
76
+ for error in errors:
77
+ print(f"error: {error}")
78
+ print("tmp_preserved=true")
79
+ return 1
80
+
81
+ temp_root = paper_root / "tmp"
82
+ removed_tmp = remove_path(temp_root)
83
+ print(
84
+ json.dumps(
85
+ {
86
+ "paper_root": str(paper_root),
87
+ "source_archive": str(source_archive),
88
+ "removed_tmp": removed_tmp,
89
+ "status": "complete",
90
+ },
91
+ ensure_ascii=False,
92
+ indent=2,
93
+ )
94
+ )
95
+ return 0
96
+
97
+
98
+ if __name__ == "__main__":
99
+ raise SystemExit(main())
@@ -29,10 +29,11 @@ def main() -> int:
29
29
  "source_archive": paper_root / "latex" / "source.tar",
30
30
  "latex_en": paper_root / "latex" / "paper-en",
31
31
  "latex_zh": paper_root / "latex" / "paper-zh",
32
+ "temp_root": paper_root / "tmp",
32
33
  "pdf_en": paper_root / "paper-en" / f"{name}-en.pdf",
33
34
  "pdf_zh": paper_root / "paper-zh" / f"{name}-zh.pdf",
34
35
  }
35
- for key in ("latex_en", "latex_zh"):
36
+ for key in ("latex_en", "latex_zh", "temp_root"):
36
37
  paths[key].mkdir(parents=True, exist_ok=True)
37
38
  paths["pdf_en"].parent.mkdir(parents=True, exist_ok=True)
38
39
  paths["pdf_zh"].parent.mkdir(parents=True, exist_ok=True)