papilot 0.0.0-stage → 0.1.0

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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -2
  3. package/README.zh-CN.md +127 -0
  4. package/bin/papilot.mjs +113 -0
  5. package/dist/apple-touch-icon.png +0 -0
  6. package/dist/assets/Inconsolatazi4-Regular-Dbtq7t_r.otf +0 -0
  7. package/dist/assets/TeXGyrePagellaX-Bold-CEtYdOXj.otf +0 -0
  8. package/dist/assets/TeXGyrePagellaX-Italic-CSuYIstn.otf +0 -0
  9. package/dist/assets/TeXGyrePagellaX-Regular-DQJjZE_L.otf +0 -0
  10. package/dist/assets/cursor-editorTextHighlight-kG3uUOwL.svg +8 -0
  11. package/dist/assets/index-BhQFCMTx.js +131 -0
  12. package/dist/assets/index-CAV3-87U.css +1 -0
  13. package/dist/assets/pdf-view-DJo0X38w.js +598 -0
  14. package/dist/assets/pdf.worker.min-CjEcRF4W.mjs +28 -0
  15. package/dist/favicon.png +0 -0
  16. package/dist/index.html +4 -0
  17. package/examples/papilot-paper/.latex-review.json +4 -0
  18. package/examples/papilot-paper/appendix/proofs.tex +60 -0
  19. package/examples/papilot-paper/appendix/reference.tex +54 -0
  20. package/examples/papilot-paper/main.tex +57 -0
  21. package/examples/papilot-paper/preamble.tex +53 -0
  22. package/examples/papilot-paper/references.bib +158 -0
  23. package/examples/papilot-paper/sections/01-introduction.tex +66 -0
  24. package/examples/papilot-paper/sections/02-using.tex +134 -0
  25. package/examples/papilot-paper/sections/03-background.tex +97 -0
  26. package/examples/papilot-paper/sections/04-model.tex +185 -0
  27. package/examples/papilot-paper/sections/05-conflicts.tex +99 -0
  28. package/examples/papilot-paper/sections/06-building.tex +70 -0
  29. package/examples/papilot-paper/sections/07-geometry.tex +193 -0
  30. package/examples/papilot-paper/sections/08-notes.tex +104 -0
  31. package/examples/papilot-paper/sections/09-agents.tex +106 -0
  32. package/examples/papilot-paper/sections/10-usage.tex +56 -0
  33. package/examples/papilot-paper/sections/11-git.tex +62 -0
  34. package/examples/papilot-paper/sections/12-security.tex +41 -0
  35. package/examples/papilot-paper/sections/13-evaluation.tex +106 -0
  36. package/examples/papilot-paper/sections/14-related.tex +38 -0
  37. package/examples/papilot-paper/sections/15-conclusion.tex +27 -0
  38. package/package.json +85 -4
  39. package/scripts/demo.ts +21 -0
  40. package/scripts/service.ts +35 -0
  41. package/src/notes.ts +40 -0
  42. package/src/server/agents.ts +343 -0
  43. package/src/server/codex.ts +63 -0
  44. package/src/server/frontend.ts +15 -0
  45. package/src/server/main.ts +202 -0
  46. package/src/server/manuscript.ts +337 -0
  47. package/src/server/paths.ts +10 -0
  48. package/src/server/pdf.ts +27 -0
  49. package/src/server/review.ts +53 -0
  50. package/src/server/security.ts +15 -0
  51. package/src/server/store.ts +60 -0
  52. package/src/server/workspace.ts +529 -0
  53. package/src/shared.ts +28 -0
  54. package/vendor/latex-review/LICENSE +21 -0
  55. package/vendor/latex-review/dist/web/assets/TeXGyrePagellaX-Bold-CEtYdOXj.otf +0 -0
  56. package/vendor/latex-review/dist/web/assets/TeXGyrePagellaX-Regular-DQJjZE_L.otf +0 -0
  57. package/vendor/latex-review/dist/web/assets/index-DyzYnIGk.css +1 -0
  58. package/vendor/latex-review/dist/web/assets/index-HxBK-dls.js +19 -0
  59. package/vendor/latex-review/dist/web/index.html +18 -0
  60. package/vendor/latex-review/package-lock.json +3114 -0
  61. package/vendor/latex-review/package.json +49 -0
  62. package/vendor/latex-review/src/cli.ts +494 -0
  63. package/vendor/latex-review/src/client.ts +198 -0
  64. package/vendor/latex-review/src/core/build.ts +296 -0
  65. package/vendor/latex-review/src/core/changes.ts +126 -0
  66. package/vendor/latex-review/src/core/config.ts +270 -0
  67. package/vendor/latex-review/src/core/diff.ts +54 -0
  68. package/vendor/latex-review/src/core/files.ts +96 -0
  69. package/vendor/latex-review/src/core/git.ts +317 -0
  70. package/vendor/latex-review/src/core/outline.ts +314 -0
  71. package/vendor/latex-review/src/core/process.ts +111 -0
  72. package/vendor/latex-review/src/core/publish.ts +339 -0
  73. package/vendor/latex-review/src/core/review.ts +734 -0
  74. package/vendor/latex-review/src/core/store.ts +66 -0
  75. package/vendor/latex-review/src/core/tasks.ts +237 -0
  76. package/vendor/latex-review/src/render/inline.ts +805 -0
  77. package/vendor/latex-review/src/render/locate.ts +481 -0
  78. package/vendor/latex-review/src/render/pdf.ts +281 -0
  79. package/vendor/latex-review/src/render/pdfdiff.ts +272 -0
  80. package/vendor/latex-review/src/render/round.ts +236 -0
  81. package/vendor/latex-review/src/render/worker.ts +10 -0
  82. package/vendor/latex-review/src/runtime.ts +23 -0
  83. package/vendor/latex-review/src/server.ts +401 -0
  84. package/vendor/latex-review/src/types.ts +283 -0
  85. package/vendor/latex-review/src/web/ChangeMap.tsx +212 -0
  86. package/vendor/latex-review/src/web/PdfStrip.tsx +77 -0
  87. package/vendor/latex-review/src/web/fonts/GUST-FONT-LICENSE.txt +29 -0
  88. package/vendor/latex-review/src/web/fonts/LPPL.txt +415 -0
  89. package/vendor/latex-review/src/web/fonts/MANIFEST-newpx.txt +20 -0
  90. package/vendor/latex-review/src/web/fonts/README-newpx.txt +473 -0
  91. package/vendor/latex-review/src/web/fonts/README.md +12 -0
  92. package/vendor/latex-review/src/web/fonts/TeXGyrePagellaX-Bold.otf +0 -0
  93. package/vendor/latex-review/src/web/fonts/TeXGyrePagellaX-Regular.otf +0 -0
  94. package/vendor/latex-review/src/web/index.html +17 -0
  95. package/vendor/latex-review/src/web/main.tsx +10 -0
  96. package/vendor/latex-review/src/web/model.ts +153 -0
  97. package/vendor/latex-review/src/web/overview.tsx +146 -0
  98. package/vendor/latex-review/src/web/review.tsx +991 -0
  99. package/vendor/latex-review/src/web/style.css +1249 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cui Chen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,127 @@
1
- # Temporary Holding Version
1
+ <div align="center">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/logo-dark.svg">
5
+ <img src="docs/assets/logo.svg" width="104" height="104" alt="Papilot logo">
6
+ </picture>
7
+
8
+ <h1>Papilot</h1>
9
+
10
+ <p><b>A Cursor-like workspace for your papers:<br>annotate and review PDFs directly without ever touching LaTeX.</b></p>
11
+
12
+ <p>English · <a href="README.zh-CN.md">简体中文</a></p>
13
+
14
+ [![CI](https://github.com/cu1ch3n/papilot/actions/workflows/ci.yml/badge.svg)](https://github.com/cu1ch3n/papilot/actions/workflows/ci.yml)
15
+ [![npm](https://img.shields.io/npm/v/papilot?color=2563eb&logo=npm)](https://www.npmjs.com/package/papilot)
16
+ [![License: MIT](https://img.shields.io/badge/license-MIT-2563eb)](LICENSE)
17
+ [![Node.js](https://img.shields.io/badge/node-%E2%89%A522.22-5FA04E?logo=nodedotjs&logoColor=white)](https://nodejs.org)
18
+ [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
19
+ [![React](https://img.shields.io/badge/React-19-087EA4?logo=react&logoColor=white)](https://react.dev)
20
+ [![LaTeX](https://img.shields.io/badge/LaTeX-008080?logo=latex&logoColor=white)](https://www.latex-project.org)
21
+ [![PRs welcome](https://img.shields.io/badge/PRs-welcome-22c55e)](https://github.com/cu1ch3n/papilot/pulls)
22
+
23
+ [![Codex](https://img.shields.io/badge/Codex-supported-10A37F?logo=openai&logoColor=white)](#-agents)
24
+ [![Claude](https://img.shields.io/badge/Claude-supported-D97757?logo=claude&logoColor=white)](#-agents)
25
+ [![Antigravity](https://img.shields.io/badge/Antigravity-supported-4285F4?logo=google&logoColor=white)](#-agents)
26
+
27
+ <br>
28
+
29
+ <img src="docs/assets/demo.gif" alt="Papilot: describe a revision, review the typeset diff, read the PDF, annotate it and commit" width="100%">
30
+
31
+ </div>
32
+
33
+ <br>
34
+
35
+ You read and mark up the PDF the way a supervisor marks up a draft. An AI agent makes the edits in LaTeX, and every change comes back to you as a before-and-after of the typeset page. Nothing touches your paper until you accept it.
36
+
37
+ ## ✨ Features
38
+
39
+ <table>
40
+ <tr>
41
+ <td width="50%" valign="top">
42
+ <img src="docs/assets/feature-note.png" alt="A note on a selected passage of the PDF">
43
+ <h3>📝 Mark up the PDF</h3>
44
+ Select a sentence or circle an equation and say what should change. SyncTeX hands the agent the exact LaTeX lines.
45
+ </td>
46
+ <td width="50%" valign="top">
47
+ <img src="docs/assets/feature-review.png" alt="A change shown before and after, with Accept and Reject">
48
+ <h3>✅ Approve change by change</h3>
49
+ Each edit comes back as a typeset diff. Accept, reject or comment on it, and undo any decision.
50
+ </td>
51
+ </tr>
52
+ <tr>
53
+ <td width="50%" valign="top">
54
+ <img src="docs/assets/feature-contents.png" alt="The PDF viewer with its contents menu open">
55
+ <h3>📖 Read it like a real PDF</h3>
56
+ Jump around with the contents menu, search every page and zoom in sharply. Selecting text picks exactly the printed words.
57
+ </td>
58
+ <td width="50%" valign="top">
59
+ <img src="docs/assets/feature-git.png" alt="The Git panel with a diff and a commit message">
60
+ <h3>🌿 Commit when you're happy</h3>
61
+ Diff files, commit the ones you choose, then pull and push without leaving the page.
62
+ </td>
63
+ </tr>
64
+ </table>
65
+
66
+ Also: 🤖 **Codex, Claude or Antigravity**, each with your own login · 🌳 **every task in its own Git worktree**, up to two at once · 📱 **runs on your computer**, and you can open it from your phone.
67
+
68
+ ## 🚀 Quick start
69
+
70
+ > [!NOTE]
71
+ > You need **Node.js 22.22+**, **Git** and a full **TeX** distribution ([MacTeX](https://www.tug.org/mactex/) or [TeX Live](https://www.tug.org/texlive/)). Revising also needs one [signed-in agent](#-agents).
72
+
73
+ ```sh
74
+ npm install -g papilot
75
+ papilot demo
76
+ ```
77
+
78
+ Open **http://127.0.0.1:4317** and choose **PDF** in the sidebar. The demo adds an example paper to revise: a 26-page LaTeX paper about Papilot itself, full of equations. When you are ready, run `papilot` and choose **Add paper** to add any LaTeX project in a Git repository.
79
+
80
+ ## 🤖 Agents
81
+
82
+ | Agent | Sign in once | How Papilot runs it |
83
+ | --- | --- | --- |
84
+ | **Codex** | `papilot codex login` | Through the Codex app server, in its workspace sandbox. Papilot uses your `codex` if you have one, or installs it on first start. |
85
+ | **Claude** | Start `papilot` with `ANTHROPIC_API_KEY` set | Through the Claude Agent SDK, downloaded the first time. The SDK does not accept a Pro or Max subscription login. |
86
+ | **Antigravity** | Install Antigravity and sign in to `agy` | `agy -p`, one process per turn, editing files only. If your region needs it, set `PAPILOT_AGY_PROXY`. |
87
+
88
+ Papilot never reads, stores or forwards your credentials. An agent that isn't installed or signed in shows as unavailable.
89
+
90
+ <details>
91
+ <summary><b>⚙️ Options and remote access</b></summary>
92
+
93
+ <br>
94
+
95
+ ```sh
96
+ papilot --port 4400 # another local port
97
+ papilot --data ~/papilot-data # keep state somewhere else (default ~/.papilot)
98
+ papilot service install # macOS: start after login, restart if it exits
99
+ npm uninstall -g papilot # remove it; your state stays in ~/.papilot until you delete it
100
+ ```
101
+
102
+ Papilot listens only on `127.0.0.1`. To use it from another device, publish it on your tailnet with Tailscale Serve and set `PAPILOT_PUBLIC_ORIGIN` and `PAPILOT_TAILSCALE_LOGIN`. Papilot then checks the host, the browser origin and the Tailscale identity of every request. The [guide](docs/guide.md#remote-access-with-tailscale) has the steps.
103
+
104
+ </details>
105
+
106
+ <details>
107
+ <summary><b>🛠️ Develop</b></summary>
108
+
109
+ <br>
110
+
111
+ ```sh
112
+ git clone https://github.com/cu1ch3n/papilot && cd papilot
113
+ npm ci && npm run setup:review
114
+ npm run build && npm start
115
+ ```
116
+
117
+ Run `npm run typecheck`, `npm test` and `npm run test:ui` before sending a pull request.
118
+
119
+ </details>
120
+
121
+ ## 📚 Documentation
122
+
123
+ The **[guide](docs/guide.md)** covers every part of the workspace, the configuration variables, recovery after restarts, and development. The example paper in [`examples/papilot-paper`](examples/papilot-paper) explains the design in depth, with proofs.
124
+
125
+ ## 📄 License
126
+
127
+ [MIT](LICENSE) © 2026 Cui Chen. The bundled review engine in [`vendor/latex-review`](vendor/latex-review) is also MIT-licensed.
@@ -0,0 +1,127 @@
1
+ <div align="center">
2
+
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/logo-dark.svg">
5
+ <img src="docs/assets/logo.svg" width="104" height="104" alt="Papilot 标志">
6
+ </picture>
7
+
8
+ <h1>Papilot</h1>
9
+
10
+ <p><b>像 Cursor 一样改论文:<br>直接在 PDF 上批注和审阅,完全不用碰 LaTeX。</b></p>
11
+
12
+ <p><a href="README.md">English</a> · 简体中文</p>
13
+
14
+ [![CI](https://github.com/cu1ch3n/papilot/actions/workflows/ci.yml/badge.svg)](https://github.com/cu1ch3n/papilot/actions/workflows/ci.yml)
15
+ [![npm](https://img.shields.io/npm/v/papilot?color=2563eb&logo=npm)](https://www.npmjs.com/package/papilot)
16
+ [![License: MIT](https://img.shields.io/badge/license-MIT-2563eb)](LICENSE)
17
+ [![Node.js](https://img.shields.io/badge/node-%E2%89%A522.22-5FA04E?logo=nodedotjs&logoColor=white)](https://nodejs.org)
18
+ [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
19
+ [![React](https://img.shields.io/badge/React-19-087EA4?logo=react&logoColor=white)](https://react.dev)
20
+ [![LaTeX](https://img.shields.io/badge/LaTeX-008080?logo=latex&logoColor=white)](https://www.latex-project.org)
21
+ [![PRs welcome](https://img.shields.io/badge/PRs-welcome-22c55e)](https://github.com/cu1ch3n/papilot/pulls)
22
+
23
+ [![Codex](https://img.shields.io/badge/Codex-supported-10A37F?logo=openai&logoColor=white)](#-智能体)
24
+ [![Claude](https://img.shields.io/badge/Claude-supported-D97757?logo=claude&logoColor=white)](#-智能体)
25
+ [![Antigravity](https://img.shields.io/badge/Antigravity-supported-4285F4?logo=google&logoColor=white)](#-智能体)
26
+
27
+ <br>
28
+
29
+ <img src="docs/assets/demo.gif" alt="Papilot:描述修改、审阅排版后的差异、阅读 PDF、在页面上批注并提交" width="100%">
30
+
31
+ </div>
32
+
33
+ <br>
34
+
35
+ 你像导师批改学生的稿子一样,直接在 PDF 上阅读和批注;AI 智能体去改 LaTeX,每一处改动都以排版后修改前后的对比回到你面前。你点头之前,论文一个字都不会变。
36
+
37
+ ## ✨ 功能
38
+
39
+ <table>
40
+ <tr>
41
+ <td width="50%" valign="top">
42
+ <img src="docs/assets/feature-note.png" alt="在 PDF 选中的段落上写批注">
43
+ <h3>📝 在 PDF 上批注</h3>
44
+ 选中一句话或框出一个公式,写下要怎么改。SyncTeX 会把对应的 LaTeX 行准确交给智能体。
45
+ </td>
46
+ <td width="50%" valign="top">
47
+ <img src="docs/assets/feature-review.png" alt="改动的修改前后对比,带接受和拒绝按钮">
48
+ <h3>✅ 逐条点头</h3>
49
+ 每处改动都以排版后的差异呈现。接受、拒绝或评论,任何决定都能撤销。
50
+ </td>
51
+ </tr>
52
+ <tr>
53
+ <td width="50%" valign="top">
54
+ <img src="docs/assets/feature-contents.png" alt="打开目录菜单的 PDF 阅读器">
55
+ <h3>📖 像本地 PDF 一样阅读</h3>
56
+ 用目录随意跳转,全文搜索,缩放清晰。选中的文字与印出的词完全对齐。
57
+ </td>
58
+ <td width="50%" valign="top">
59
+ <img src="docs/assets/feature-git.png" alt="带差异和提交信息的 Git 面板">
60
+ <h3>🌿 满意了再提交</h3>
61
+ 查看差异,只提交你勾选的文件,pull 和 push 都不用离开页面。
62
+ </td>
63
+ </tr>
64
+ </table>
65
+
66
+ 此外:🤖 **Codex、Claude 或 Antigravity**,各用你自己的登录 · 🌳 **每个任务都有独立的 Git 工作树**,最多两个同时运行 · 📱 **运行在你的电脑上**,也能用手机打开。
67
+
68
+ ## 🚀 快速开始
69
+
70
+ > [!NOTE]
71
+ > 需要 **Node.js 22.22+**、**Git** 和完整的 **TeX** 发行版([MacTeX](https://www.tug.org/mactex/) 或 [TeX Live](https://www.tug.org/texlive/))。修改论文还需要至少一个[已登录的智能体](#-智能体)。
72
+
73
+ ```sh
74
+ npm install -g papilot
75
+ papilot demo
76
+ ```
77
+
78
+ 打开 **http://127.0.0.1:4317**,在侧栏选择 **PDF**。demo 会添加一篇示例论文供你修改:一篇讲 Papilot 本身的 26 页 LaTeX 论文,满是公式。准备好之后,运行 `papilot`,选择 **Add paper**,即可添加任何放在 Git 仓库里的 LaTeX 项目。
79
+
80
+ ## 🤖 智能体
81
+
82
+ | 智能体 | 登录(一次即可) | Papilot 如何运行它 |
83
+ | --- | --- | --- |
84
+ | **Codex** | `papilot codex login` | 通过 Codex app server 在其工作区沙箱中运行;已装有 `codex` 就直接用,没有则在首次启动时自动安装。 |
85
+ | **Claude** | 设置好 `ANTHROPIC_API_KEY` 再启动 `papilot` | 通过 Claude Agent SDK 运行,首次使用时下载;该 SDK 不接受 Pro 或 Max 订阅登录。 |
86
+ | **Antigravity** | 安装 Antigravity 并登录 `agy` | 运行 `agy -p`,每轮一个进程,只编辑文件;所在地区需要代理时设置 `PAPILOT_AGY_PROXY`。 |
87
+
88
+ Papilot 从不读取、保存或转发你的凭据。没有安装或没有登录的智能体会显示为不可用。
89
+
90
+ <details>
91
+ <summary><b>⚙️ 选项与远程访问</b></summary>
92
+
93
+ <br>
94
+
95
+ ```sh
96
+ papilot --port 4400 # 使用其他本地端口
97
+ papilot --data ~/papilot-data # 把状态放在别处(默认 ~/.papilot)
98
+ papilot service install # macOS:登录后自动启动,退出后自动重启
99
+ npm uninstall -g papilot # 卸载;状态仍保留在 ~/.papilot,可自行删除
100
+ ```
101
+
102
+ Papilot 只监听 `127.0.0.1`。若要从其他设备访问,用 Tailscale Serve 把它发布到你的 tailnet,并设置 `PAPILOT_PUBLIC_ORIGIN` 和 `PAPILOT_TAILSCALE_LOGIN`;Papilot 会校验每个请求的主机、浏览器来源和 Tailscale 身份。具体步骤见[指南](docs/guide.md#remote-access-with-tailscale)。
103
+
104
+ </details>
105
+
106
+ <details>
107
+ <summary><b>🛠️ 参与开发</b></summary>
108
+
109
+ <br>
110
+
111
+ ```sh
112
+ git clone https://github.com/cu1ch3n/papilot && cd papilot
113
+ npm ci && npm run setup:review
114
+ npm run build && npm start
115
+ ```
116
+
117
+ 提交 pull request 前,请运行 `npm run typecheck`、`npm test` 和 `npm run test:ui`。
118
+
119
+ </details>
120
+
121
+ ## 📚 文档
122
+
123
+ **[指南](docs/guide.md)**(英文)介绍工作台的每个部分、配置变量、重启后的恢复以及开发流程。[`examples/papilot-paper`](examples/papilot-paper) 中的示例论文深入讲解了设计,并附有证明。
124
+
125
+ ## 📄 许可证
126
+
127
+ [MIT](LICENSE) © 2026 Cui Chen。内置的审阅引擎 [`vendor/latex-review`](vendor/latex-review) 同样采用 MIT 许可证。
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ // The papilot command: starts the workspace, or adds the example paper first.
3
+ import { spawn, spawnSync } from 'node:child_process';
4
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const usage = `Usage: papilot [demo | codex <args> | service install | service stop] [--port <number>] [--data <folder>]
10
+
11
+ papilot Start Papilot at http://127.0.0.1:4317
12
+ papilot demo Add the example paper, then start
13
+ papilot codex login Sign in to Codex with the Codex that Papilot uses
14
+ papilot service install Run Papilot in the background after login (macOS)
15
+ papilot service stop Stop the background service
16
+ --port <number> Listen on another local port (PAPILOT_PORT)
17
+ --data <folder> Keep state in another folder (PAPILOT_DATA, default ~/.papilot)
18
+ --version Print the version`;
19
+
20
+ const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
21
+ const args = process.argv.slice(2);
22
+ let demo = false, service = '', codexArgs = null;
23
+ for (let i = 0; i < args.length; i++) {
24
+ const arg = args[i];
25
+ if (arg === 'demo') demo = true;
26
+ else if (arg === 'codex') { codexArgs = args.slice(i + 1); break; }
27
+ else if (arg === 'service' && ['install', 'stop'].includes(args[i + 1])) service = args[++i];
28
+ else if (arg === '--port') {
29
+ const port = Number(args[++i]);
30
+ if (!Number.isInteger(port) || port < 1 || port > 65535) { console.error('--port needs a number from 1 to 65535.'); process.exit(1); }
31
+ process.env.PAPILOT_PORT = String(port);
32
+ }
33
+ else if (arg === '--data' && args[i + 1]) process.env.PAPILOT_DATA = path.resolve(args[++i]);
34
+ else if (arg === '--version' || arg === '-v') {
35
+ console.log(pkg.version);
36
+ process.exit(0);
37
+ } else {
38
+ if (arg !== '--help' && arg !== '-h') console.error(`Unknown argument: ${arg}\n`);
39
+ console.log(usage);
40
+ process.exit(arg === '--help' || arg === '-h' ? 0 : 1);
41
+ }
42
+ }
43
+
44
+ // Papilot needs Node 22.22+. When an older Node runs this command, use a newer
45
+ // one from nvm or PATH, preferring LTS releases, without changing the default.
46
+ const parse = version => version.replace(/^v/, '').split('.').map(Number);
47
+ const supported = version => { const [major, minor] = parse(version); return major > 22 || (major === 22 && minor >= 22); };
48
+ if (!supported(process.versions.node)) {
49
+ const versions = path.join(process.env.NVM_DIR || path.join(os.homedir(), '.nvm'), 'versions/node');
50
+ const nvm = existsSync(versions) ? readdirSync(versions).map(v => ({ file: path.join(versions, v, 'bin/node'), version: v })) : [];
51
+ const others = [...new Set([...(process.env.PATH || '').split(path.delimiter).map(dir => path.join(dir, 'node')), '/opt/homebrew/bin/node', '/usr/local/bin/node'])]
52
+ .filter(file => existsSync(file)).map(file => ({ file, version: (spawnSync(file, ['--version'], { encoding: 'utf8' }).stdout || '0').trim() }));
53
+ // nvm's versions come first, then any other Node; even majors are LTS releases,
54
+ // and among those the newest wins.
55
+ const rank = ({ version }) => { const [major, minor, patch] = parse(version); return (major % 2 === 0 ? 1e9 : 0) + major * 1e6 + minor * 1e3 + patch; };
56
+ const best = list => list.filter(c => existsSync(c.file) && supported(c.version)).sort((a, b) => rank(b) - rank(a))[0];
57
+ const node = best(nvm) || best(others);
58
+ if (!node) {
59
+ console.error(`Papilot needs Node.js 22.22 or newer; this is ${process.versions.node}. Install it with \`nvm install 22\` or from https://nodejs.org, then run papilot again.`);
60
+ process.exit(1);
61
+ }
62
+ console.error(`Papilot needs Node.js 22.22+; using ${node.version.replace(/^v/, '')} from ${path.dirname(node.file)}.`);
63
+ const child = spawn(node.file, [fileURLToPath(import.meta.url), ...args], { stdio: 'inherit', env: { ...process.env, PATH: path.dirname(node.file) + path.delimiter + process.env.PATH } });
64
+ // Pass stop signals on, so stopping papilot also stops the server it started.
65
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) process.on(signal, () => child.kill(signal));
66
+ await new Promise(() => child.on('exit', code => process.exit(code ?? 1)));
67
+ }
68
+ const tex = !spawnSync('latexmk', ['-v'], { stdio: 'ignore' }).error;
69
+
70
+ // Node 22 marks node:sqlite as experimental; that notice is noise for users.
71
+ const emitWarning = process.emitWarning;
72
+ process.emitWarning = (warning, ...rest) => String(warning).includes('SQLite') ? undefined : emitWarning.call(process, warning, ...rest);
73
+ process.env.PAPILOT_DATA ||= path.join(os.homedir(), '.papilot');
74
+
75
+ // Papilot drives Codex through its app server. A codex on PATH at least as new
76
+ // as the tested version is used as it is; otherwise that version is installed
77
+ // once into the data folder.
78
+ function findCodex() {
79
+ if (process.env.PAPILOT_CODEX_BIN) return process.env.PAPILOT_CODEX_BIN;
80
+ const tested = pkg.devDependencies['@openai/codex'];
81
+ const version = file => (spawnSync(file, ['--version'], { encoding: 'utf8' }).stdout || '').match(/\d+\.\d+\.\d+/)?.[0] || '0.0.0';
82
+ const recent = file => { const [a, b] = [parse(version(file)), parse(tested)]; return (a[0] - b[0] || a[1] - b[1] || a[2] - b[2]) >= 0; };
83
+ const onPath = (process.env.PATH || '').split(path.delimiter).map(dir => path.join(dir, 'codex')).find(file => existsSync(file));
84
+ if (onPath && recent(onPath)) return onPath;
85
+ const prefix = path.join(process.env.PAPILOT_DATA, 'agents'), installed = path.join(prefix, 'node_modules/.bin/codex');
86
+ if (existsSync(installed) && recent(installed)) return installed;
87
+ console.log(`Installing Codex ${tested} for Papilot (first run only)...`);
88
+ const result = spawnSync('npm', ['install', '--prefix', prefix, `@openai/codex@${tested}`, '--no-audit', '--no-fund', '--loglevel=error'], { stdio: 'inherit' });
89
+ if (result.status === 0 && existsSync(installed)) return installed;
90
+ console.warn('Codex could not be installed, so Codex tasks are unavailable. Install it with npm install -g @openai/codex and run papilot again.');
91
+ return '';
92
+ }
93
+ if (codexArgs) {
94
+ const codex = findCodex();
95
+ process.exit(codex ? spawnSync(codex, codexArgs, { stdio: 'inherit' }).status ?? 1 : 1);
96
+ }
97
+ if (service !== 'stop') process.env.PAPILOT_CODEX_BIN = findCodex() || 'codex';
98
+ const { register } = await import('tsx/esm/api');
99
+ register();
100
+ if (service) {
101
+ process.argv.splice(2, Infinity, service);
102
+ await import('../scripts/service.ts');
103
+ } else {
104
+ if (demo) await import('../scripts/demo.ts');
105
+ try {
106
+ await import('../src/server/main.ts');
107
+ } catch (error) {
108
+ if (error?.code !== 'EADDRINUSE') throw error;
109
+ console.error(`Port ${process.env.PAPILOT_PORT || 4317} is already in use. Is Papilot already running? Start another copy with --port <number>.`);
110
+ process.exit(1);
111
+ }
112
+ if (!tex) console.warn('\nlatexmk was not found, so PDFs cannot be built yet. Install MacTeX or TeX Live (the full distribution), open a new terminal and run papilot again.');
113
+ }
Binary file
@@ -0,0 +1,8 @@
1
+ <svg width="29" height="32" viewBox="0 0 29 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M28 16.75C28.2761 16.75 28.5 16.5261 28.5 16.25V15C28.5 14.7239 28.2761 14.5 28 14.5H26.358C25.9117 14.5 25.4773 14.6257 25.0999 14.8604L25.0989 14.8611L24 15.5484L22.9 14.861L22.8991 14.8604C22.5218 14.6257 22.0875 14.5 21.642 14.5H20C19.7239 14.5 19.5 14.7239 19.5 15V16.25C19.5 16.5261 19.7239 16.75 20 16.75H21.642C21.6648 16.75 21.6885 16.7564 21.7101 16.7697C21.7102 16.7698 21.7104 16.7699 21.7105 16.77L22.817 17.461C22.817 17.461 22.8171 17.4611 22.8171 17.4611C22.8171 17.4611 22.8171 17.4611 22.8171 17.4611C22.8552 17.4849 22.876 17.5229 22.876 17.567V22.625V27.683C22.876 27.7271 22.8552 27.765 22.8172 27.7889C22.8171 27.7889 22.8171 27.789 22.817 27.789L21.7095 28.48C21.7094 28.4801 21.7093 28.4802 21.7092 28.4803C21.6872 28.4938 21.6644 28.5 21.641 28.5H20C19.7239 28.5 19.5 28.7239 19.5 29V30.25C19.5 30.5261 19.7239 30.75 20 30.75H21.642C22.0883 30.75 22.5227 30.6243 22.9001 30.3896L22.9009 30.3891L24 29.7026L25.1 30.39L25.1009 30.3906C25.4783 30.6253 25.9127 30.751 26.359 30.751H28C28.2761 30.751 28.5 30.5271 28.5 30.251V29.001C28.5 28.7249 28.2761 28.501 28 28.501H26.358C26.3352 28.501 26.3115 28.4946 26.2899 28.4813C26.2897 28.4812 26.2896 28.4811 26.2895 28.481L25.183 27.79C25.183 27.79 25.183 27.79 25.1829 27.79C25.1829 27.7899 25.1829 27.7899 25.1829 27.7899C25.1462 27.7669 25.125 27.7297 25.125 27.684V22.625V17.567C25.125 17.5227 25.146 17.4844 25.1836 17.4606C25.1838 17.4605 25.1839 17.4604 25.184 17.4603L26.2895 16.77C26.2896 16.7699 26.2898 16.7698 26.2899 16.7697C26.3119 16.7562 26.3346 16.75 26.358 16.75H28Z" fill="black" stroke="#FBFBFE" stroke-linejoin="round"/>
3
+ <path d="M24.625 17.567C24.625 17.35 24.735 17.152 24.918 17.037L26.026 16.345C26.126 16.283 26.24 16.25 26.358 16.25H28V15H26.358C26.006 15 25.663 15.099 25.364 15.285L24.256 15.978C24.161 16.037 24.081 16.113 24 16.187C23.918 16.113 23.839 16.037 23.744 15.978L22.635 15.285C22.336 15.099 21.993 15 21.642 15H20V16.25H21.642C21.759 16.25 21.874 16.283 21.974 16.345L23.082 17.037C23.266 17.152 23.376 17.35 23.376 17.567V22.625V27.683C23.376 27.9 23.266 28.098 23.082 28.213L21.973 28.905C21.873 28.967 21.759 29 21.641 29H20V30.25H21.642C21.994 30.25 22.337 30.151 22.636 29.965L23.744 29.273C23.84 29.213 23.919 29.137 24 29.064C24.081 29.137 24.161 29.213 24.256 29.273L25.365 29.966C25.664 30.152 26.007 30.251 26.359 30.251H28V29.001H26.358C26.241 29.001 26.126 28.968 26.026 28.906L24.918 28.214C24.734 28.099 24.625 27.901 24.625 27.684V22.625V17.567Z" fill="black"/>
4
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.2 2.59C12.28 2.51 12.43 2.5 12.43 2.5C12.48 2.5 12.58 2.52 12.66 2.6L14.45 4.39C14.58 4.52 14.58 4.72 14.45 4.85L11.7713 7.52872L9.51628 5.27372L12.2 2.59ZM13.2658 4.62L11.7713 6.1145L10.9305 5.27372L12.425 3.77921L13.2658 4.62Z" fill="#FBFBFE"/>
5
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M5.98 8.82L8.23 11.07L10.7106 8.58938L8.45562 6.33438L5.98 8.81V8.82ZM8.23 9.65579L9.29641 8.58938L8.45562 7.74859L7.38921 8.815L8.23 9.65579Z" fill="#FBFBFE"/>
6
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10.1526 12.6816L16.2125 6.6217C16.7576 6.08919 17.05 5.3707 17.05 4.62C17.05 3.86931 16.7576 3.15084 16.2126 2.61834L14.4317 0.837474C13.8992 0.29242 13.1807 0 12.43 0C11.6643 0 10.9529 0.312929 10.4329 0.832893L3.68289 7.58289C3.04127 8.22452 3.00459 9.25075 3.57288 9.93634L1.29187 12.2239C1.09186 12.4245 0.990263 12.6957 1.0007 12.9685L1 14C0.447715 14 0 14.4477 0 15V17C0 17.5523 0.447715 18 1 18H16C16.5523 18 17 17.5523 17 17V15C17 14.4477 16.5523 14 16 14H10.2325C9.83594 14 9.39953 13.4347 10.1526 12.6816ZM4.39 9.35L4.9807 9.9407L2.39762 12.5312H6.63877L7.10501 12.065L7.57125 12.5312H8.88875L15.51 5.91C15.86 5.57 16.05 5.11 16.05 4.62C16.05 4.13 15.86 3.67 15.51 3.33L13.72 1.54C13.38 1.19 12.92 1 12.43 1C11.94 1 11.48 1.2 11.14 1.54L4.39 8.29C4.1 8.58 4.1 9.06 4.39 9.35ZM16 17V15H1V17H16Z" fill="#FBFBFE"/>
7
+ <path d="M15.1616 5.55136L15.1616 5.55132L15.1564 5.55645L8.40645 12.3064C8.35915 12.3537 8.29589 12.38 8.23 12.38C8.16411 12.38 8.10085 12.3537 8.05355 12.3064L7.45857 11.7115L7.10501 11.3579L6.75146 11.7115L6.03289 12.43H3.20465L5.33477 10.2937L5.6873 9.94019L5.33426 9.58715L4.74355 8.99645C4.64882 8.90171 4.64882 8.73829 4.74355 8.64355L11.4936 1.89355C11.7436 1.64354 12.0779 1.5 12.43 1.5C12.7883 1.5 13.1179 1.63776 13.3614 1.88839L13.3613 1.88843L13.3664 1.89355L15.1564 3.68355L15.1564 3.68359L15.1616 3.68864C15.4122 3.93211 15.55 4.26166 15.55 4.62C15.55 4.97834 15.4122 5.30789 15.1616 5.55136ZM5.48 8.82V9.02711L5.62645 9.17355L7.87645 11.4236L8.23 11.7771L8.58355 11.4236L11.0642 8.94293L11.4177 8.58938L11.0642 8.23582L8.80918 5.98082L8.45562 5.62727L8.10207 5.98082L5.62645 8.45645L5.48 8.60289V8.81V8.82ZM11.4177 7.88227L11.7713 8.23582L12.1248 7.88227L14.8036 5.20355C15.1288 4.87829 15.1288 4.36171 14.8036 4.03645L13.0136 2.24645C12.8186 2.05146 12.5792 2 12.43 2H12.4134L12.3967 2.00111L12.43 2.5C12.3967 2.00111 12.3966 2.00112 12.3965 2.00112L12.3963 2.00114L12.3957 2.00117L12.3947 2.00125L12.3924 2.00142L12.387 2.00184L12.3732 2.00311C12.3628 2.00416 12.3498 2.00567 12.3346 2.00784C12.3049 2.01208 12.2642 2.01925 12.2178 2.03146C12.1396 2.05202 11.9797 2.10317 11.8464 2.23645L9.16273 4.92016L8.80918 5.27372L9.16273 5.62727L11.4177 7.88227ZM1.5 16.5V15.5H15.5V16.5H1.5Z" stroke="#15141A"/>
8
+ </svg>