@aloth/olcli 0.6.0 → 0.7.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.
- package/README.md +91 -441
- package/dist/mcp.js +1 -1
- package/dist/remote-helper.d.ts +16 -0
- package/dist/remote-helper.d.ts.map +1 -0
- package/dist/remote-helper.js +431 -0
- package/dist/remote-helper.js.map +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
**Command-line interface for Overleaf** — Sync, manage, and compile LaTeX projects from your terminal.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@aloth/olcli)
|
|
6
|
-
[](https://www.npmjs.com/package/@aloth/olcli)
|
|
7
|
+
[](https://github.com/aloth/olcli)
|
|
7
8
|
[](https://opensource.org/licenses/MIT)
|
|
8
9
|
[](https://agentskills.io)
|
|
9
10
|
|
|
@@ -15,31 +16,30 @@ Work with Overleaf projects directly from your command line. Edit locally with y
|
|
|
15
16
|
|
|
16
17
|
## Features
|
|
17
18
|
|
|
18
|
-
**Full Overleaf command-line access:**
|
|
19
|
-
|
|
20
19
|
- 📋 **List** all your Overleaf projects
|
|
21
20
|
- ⬇️ **Pull** project files to local directory for offline editing
|
|
22
21
|
- ⬆️ **Push** local changes back to Overleaf
|
|
23
22
|
- 🔄 **Sync** bidirectionally with smart conflict detection
|
|
24
|
-
-
|
|
23
|
+
- 🔀 **Git remote** — use Overleaf as a native git remote ([docs](docs/GIT-REMOTE.md))
|
|
24
|
+
- ✌️ **Two-way deletions** — files removed locally are deleted on Overleaf on next sync
|
|
25
25
|
- 🗑️ **Delete** and ✏️ **rename** remote files by path
|
|
26
|
-
- 🚫 **Smart ignore** — LaTeX build artifacts
|
|
26
|
+
- 🚫 **Smart ignore** — LaTeX build artifacts and OS noise filtered automatically; extend with `.olignore`
|
|
27
27
|
- 📄 **Compile** PDFs using Overleaf's remote compiler
|
|
28
28
|
- 📦 **Download** individual files or full project archives
|
|
29
29
|
- 📤 **Upload** files to projects
|
|
30
|
-
- 💬 **Review comments** — list
|
|
30
|
+
- 💬 **Review comments** — list, add, resolve, reopen, delete, and reply to threads
|
|
31
31
|
- 🗂️ **Preserve folder structure** when pushing nested files
|
|
32
|
-
- ⏱️ **Configurable timeout**
|
|
33
|
-
- 🔑 **Password login**
|
|
34
|
-
- ⚙️ **
|
|
32
|
+
- ⏱️ **Configurable timeout** for slow connections
|
|
33
|
+
- 🔑 **Password login** for self-hosted instances (no browser required)
|
|
34
|
+
- ⚙️ **Self-hosted Overleaf/ShareLaTeX** support
|
|
35
35
|
- 📊 **Output** compile artifacts (`.bbl`, `.log`, `.aux` for arXiv submissions)
|
|
36
|
+
- 🤖 **MCP server** for AI assistants ([docs](docs/MCP.md))
|
|
36
37
|
|
|
37
38
|
**Perfect for:**
|
|
38
39
|
- Editing LaTeX in your preferred text editor (Vim, VS Code, Emacs, etc.)
|
|
39
40
|
- Version control with Git while using Overleaf's compiler
|
|
40
41
|
- Automating workflows and CI/CD pipelines
|
|
41
42
|
- Offline work with periodic sync
|
|
42
|
-
- Collaborative projects where some prefer CLI, others prefer web
|
|
43
43
|
|
|
44
44
|
## Installation
|
|
45
45
|
|
|
@@ -52,8 +52,6 @@ brew install olcli
|
|
|
52
52
|
|
|
53
53
|
### npm (all platforms)
|
|
54
54
|
|
|
55
|
-
Install globally to use the `olcli` command anywhere:
|
|
56
|
-
|
|
57
55
|
```bash
|
|
58
56
|
npm install -g @aloth/olcli
|
|
59
57
|
```
|
|
@@ -70,95 +68,53 @@ npx @aloth/olcli list
|
|
|
70
68
|
npx skills add aloth/olcli
|
|
71
69
|
```
|
|
72
70
|
|
|
73
|
-
### Arch Linux
|
|
74
|
-
|
|
75
|
-
The package is available on the [Arch User Repository (AUR)](https://aur.archlinux.org/packages/olcli).
|
|
76
|
-
|
|
77
|
-
You can install it using your preferred AUR helper (such as `yay` or `paru`):
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
yay -S olcli
|
|
81
|
-
# or
|
|
82
|
-
paru -S olcli
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
#### Manual Installation:
|
|
86
|
-
If you prefer not to use an AUR helper, you can build and install the package manually using makepkg:
|
|
87
|
-
code Bash
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
git clone https://aur.archlinux.org/olcli.git
|
|
91
|
-
cd olcli
|
|
92
|
-
makepkg -si
|
|
93
|
-
```
|
|
94
|
-
|
|
95
71
|
## Quick Start
|
|
96
72
|
|
|
97
|
-
### 1. Authenticate
|
|
73
|
+
### 1. Authenticate
|
|
98
74
|
|
|
99
|
-
**
|
|
100
|
-
|
|
101
|
-
1. Log into [overleaf.com](https://www.overleaf.com)
|
|
102
|
-
2. Open Developer Tools (F12 or Cmd+Option+I) → Application/Storage → Cookies
|
|
103
|
-
3. Copy the value of `overleaf_session2`
|
|
75
|
+
**Session cookie** (overleaf.com and self-hosted):
|
|
104
76
|
|
|
105
77
|
```bash
|
|
106
78
|
olcli auth --cookie "your_session_cookie_value"
|
|
107
79
|
```
|
|
108
80
|
|
|
109
|
-
**
|
|
81
|
+
**Email/password** (self-hosted without reCAPTCHA):
|
|
110
82
|
|
|
111
83
|
```bash
|
|
112
84
|
olcli auth --email "you@example.com" --password "your_password"
|
|
113
85
|
```
|
|
114
86
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
**Tip:** The cookie stays valid for weeks. Just refresh it when authentication fails.
|
|
118
|
-
|
|
119
|
-
### 2. List Your Projects
|
|
87
|
+
### 2. List Projects
|
|
120
88
|
|
|
121
89
|
```bash
|
|
122
90
|
olcli list
|
|
123
91
|
```
|
|
124
92
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
### 3. Pull a Project Locally
|
|
128
|
-
|
|
129
|
-
Download any project to work on it locally:
|
|
93
|
+
### 3. Pull, Edit, Push
|
|
130
94
|
|
|
131
95
|
```bash
|
|
132
96
|
olcli pull "My Thesis"
|
|
133
97
|
cd My_Thesis/
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Now you can edit `.tex` files with your preferred editor (Vim, VS Code, Emacs, etc.).
|
|
137
|
-
|
|
138
|
-
### 4. Edit Locally, Sync to Overleaf
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
# Edit files locally with your favorite editor
|
|
142
98
|
vim main.tex
|
|
143
|
-
|
|
144
|
-
# Push changes back to Overleaf
|
|
145
99
|
olcli push
|
|
146
|
-
|
|
147
|
-
# Or sync bidirectionally (pull + push in one command)
|
|
148
|
-
olcli sync
|
|
149
100
|
```
|
|
150
101
|
|
|
151
|
-
|
|
102
|
+
### 4. Compile PDF
|
|
152
103
|
|
|
153
|
-
|
|
104
|
+
```bash
|
|
105
|
+
olcli pdf
|
|
106
|
+
```
|
|
154
107
|
|
|
155
|
-
|
|
108
|
+
### 5. Or use native git commands
|
|
156
109
|
|
|
157
110
|
```bash
|
|
158
|
-
|
|
111
|
+
git clone overleaf::https://www.overleaf.com/project/<id>
|
|
112
|
+
cd <project>
|
|
113
|
+
# edit, commit, push — standard git workflow
|
|
114
|
+
git push
|
|
159
115
|
```
|
|
160
116
|
|
|
161
|
-
|
|
117
|
+
See [Git Remote Helper docs](docs/GIT-REMOTE.md) for details.
|
|
162
118
|
|
|
163
119
|
## Commands
|
|
164
120
|
|
|
@@ -176,302 +132,142 @@ All commands auto-detect the project when run from a synced directory (contains
|
|
|
176
132
|
| `olcli sync [dir]` | Bidirectional sync (pull + push) |
|
|
177
133
|
| `olcli upload <file> [project]` | Upload a single file |
|
|
178
134
|
| `olcli download <file> [project]` | Download a single file |
|
|
179
|
-
| `olcli
|
|
180
|
-
| `olcli
|
|
181
|
-
| `olcli comments resolve <threadId> [project]` | Resolve a comment thread |
|
|
182
|
-
| `olcli comments reply <threadId> <body> [project]` | Reply to a comment thread |
|
|
183
|
-
| `olcli comments reopen <threadId> [project]` | Reopen a resolved comment thread |
|
|
184
|
-
| `olcli comments delete <threadId> [project]` | Permanently delete a comment thread |
|
|
185
|
-
| `olcli delete <file> [project]` | Delete a remote file or folder by path (alias: `rm`) |
|
|
186
|
-
| `olcli rename <oldname> <newname> [project]` | Rename a remote file or folder by path (alias: `mv`) |
|
|
187
|
-
| `olcli ignored [dir]` | List ignore patterns currently in effect |
|
|
188
|
-
| `olcli zip [project]` | Download project as zip archive |
|
|
135
|
+
| `olcli delete <file> [project]` | Delete a remote file or folder (alias: `rm`) |
|
|
136
|
+
| `olcli rename <old> <new> [project]` | Rename a remote file or folder (alias: `mv`) |
|
|
189
137
|
| `olcli compile [project]` | Trigger PDF compilation |
|
|
190
138
|
| `olcli pdf [project]` | Compile and download PDF |
|
|
191
139
|
| `olcli output [type]` | Download compile output files |
|
|
192
|
-
| `olcli
|
|
193
|
-
| `olcli
|
|
194
|
-
| `olcli
|
|
195
|
-
| `olcli
|
|
140
|
+
| `olcli zip [project]` | Download project as zip archive |
|
|
141
|
+
| `olcli comments list [project]` | List comments (`--status`, `--context`) |
|
|
142
|
+
| `olcli comments add <file> <msg>` | Add a comment to selected text |
|
|
143
|
+
| `olcli comments reply <id> <body>` | Reply to a comment thread |
|
|
144
|
+
| `olcli comments resolve <id>` | Resolve a comment thread |
|
|
145
|
+
| `olcli comments reopen <id>` | Reopen a resolved thread |
|
|
146
|
+
| `olcli comments delete <id>` | Delete a comment thread |
|
|
147
|
+
| `olcli ignored [dir]` | List ignore patterns in effect |
|
|
148
|
+
| `olcli config set-url <url>` | Set self-hosted base URL |
|
|
149
|
+
| `olcli config set-cookie-name <name>` | Set session cookie name |
|
|
150
|
+
| `olcli config set-timeout <ms>` | Set default HTTP timeout |
|
|
196
151
|
| `olcli check` | Show config paths and credential sources |
|
|
197
152
|
|
|
198
|
-
###
|
|
199
|
-
|
|
200
|
-
```bash
|
|
201
|
-
olcli comments list "My Paper" --status open --context 2
|
|
202
|
-
olcli comments list "My Paper" --status resolved --json
|
|
203
|
-
olcli comments add main.tex "Please clarify this definition" "My Paper" --text "A Skill is"
|
|
204
|
-
olcli comments add main.tex "Check this sentence" "My Paper" --line 42 --column 1 --length 20 --json
|
|
205
|
-
olcli comments reply 6a1a5fedbf90b811e1000001 "Good point, I'll fix this" "My Paper"
|
|
206
|
-
olcli comments resolve 6a1a5fedbf90b811e1000001 "My Paper" --json
|
|
207
|
-
olcli comments reopen 6a1a5fedbf90b811e1000001 "My Paper"
|
|
208
|
-
olcli comments delete 6a1a5fedbf90b811e1000001 "My Paper" --json
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Global options
|
|
212
|
-
|
|
213
|
-
These flags work with **every** command and may be placed before or after the command name:
|
|
153
|
+
### Global Options
|
|
214
154
|
|
|
215
155
|
| Flag | Description |
|
|
216
156
|
|------|-------------|
|
|
217
|
-
| `--verbose` | Print
|
|
218
|
-
| `--base-url <url>` | Override
|
|
219
|
-
| `--cookie-name <name>` | Override
|
|
220
|
-
| `--timeout <ms>` | Override
|
|
221
|
-
|
|
222
|
-
Examples:
|
|
223
|
-
|
|
224
|
-
```bash
|
|
225
|
-
olcli --verbose pdf # see every request the compile makes
|
|
226
|
-
olcli pdf --verbose # same thing, flag after command
|
|
227
|
-
olcli --verbose sync # debug a sync that's misbehaving
|
|
228
|
-
olcli --verbose upload figures/a.png # confirm the file is placed in figures/
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
## Use Cases
|
|
232
|
-
|
|
233
|
-
### Local Editing with Overleaf Compilation
|
|
234
|
-
|
|
235
|
-
Work offline in your favorite editor, push when ready, compile remotely:
|
|
236
|
-
|
|
237
|
-
```bash
|
|
238
|
-
olcli pull "Research Paper"
|
|
239
|
-
cd Research_Paper
|
|
240
|
-
vim introduction.tex
|
|
241
|
-
git commit -am "Update intro"
|
|
242
|
-
olcli push
|
|
243
|
-
olcli pdf
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Git Version Control + Overleaf
|
|
247
|
-
|
|
248
|
-
Keep your LaTeX project in Git while using Overleaf's compiler:
|
|
249
|
-
|
|
250
|
-
```bash
|
|
251
|
-
olcli pull "My Thesis" thesis
|
|
252
|
-
cd thesis
|
|
253
|
-
git init
|
|
254
|
-
git add .
|
|
255
|
-
git commit -m "Initial import from Overleaf"
|
|
256
|
-
|
|
257
|
-
# Daily workflow
|
|
258
|
-
vim chapters/methods.tex
|
|
259
|
-
git commit -am "Draft methods section"
|
|
260
|
-
olcli sync # Sync with Overleaf
|
|
261
|
-
olcli pdf
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
### Automated Workflows
|
|
265
|
-
|
|
266
|
-
Integrate Overleaf compilation into CI/CD:
|
|
267
|
-
|
|
268
|
-
```bash
|
|
269
|
-
#!/bin/bash
|
|
270
|
-
olcli auth --cookie "$OVERLEAF_SESSION"
|
|
271
|
-
olcli pull "Automated Report"
|
|
272
|
-
./generate-data.py > tables/results.tex
|
|
273
|
-
olcli push
|
|
274
|
-
olcli pdf -o report-$(date +%Y-%m-%d).pdf
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### arXiv Submissions
|
|
278
|
-
|
|
279
|
-
Download the `.bbl` file for arXiv submissions:
|
|
280
|
-
|
|
281
|
-
```bash
|
|
282
|
-
olcli output bbl --project "My Paper"
|
|
283
|
-
# Downloads: bbl
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
List all available compile output files:
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
olcli output --list
|
|
290
|
-
# Available output files:
|
|
291
|
-
# aux output.aux
|
|
292
|
-
# bbl output.bbl
|
|
293
|
-
# blg output.blg
|
|
294
|
-
# log output.log
|
|
295
|
-
# ...
|
|
296
|
-
```
|
|
157
|
+
| `--verbose` | Print HTTP requests and responses to stderr |
|
|
158
|
+
| `--base-url <url>` | Override Overleaf instance URL |
|
|
159
|
+
| `--cookie-name <name>` | Override session cookie name |
|
|
160
|
+
| `--timeout <ms>` | Override HTTP timeout (default: 10000) |
|
|
297
161
|
|
|
298
162
|
## Sync Behavior
|
|
299
163
|
|
|
300
164
|
### Pull
|
|
301
165
|
- Downloads all files from Overleaf
|
|
302
|
-
-
|
|
166
|
+
- Skips local files modified after last pull (won't overwrite your changes)
|
|
303
167
|
- Use `--force` to overwrite local changes
|
|
304
168
|
|
|
305
169
|
### Push
|
|
306
170
|
- Uploads files modified after last pull
|
|
307
|
-
- Preserves nested folder structure
|
|
308
|
-
- Filters out LaTeX build artifacts and OS noise
|
|
309
|
-
- Use `--all` to upload all files
|
|
310
|
-
- Use `--dry-run` to preview changes
|
|
311
|
-
- Use `--show-ignored` to see what was filtered out
|
|
171
|
+
- Preserves nested folder structure
|
|
172
|
+
- Filters out LaTeX build artifacts and OS noise
|
|
173
|
+
- Use `--all` to upload all files, `--dry-run` to preview
|
|
312
174
|
|
|
313
175
|
### Sync
|
|
314
|
-
- Pulls remote changes
|
|
315
|
-
-
|
|
316
|
-
-
|
|
317
|
-
- **Propagates local deletions to the remote** — if you delete a file locally, it's deleted on Overleaf on the next sync. Use `--no-delete` to opt out.
|
|
318
|
-
- Filters out LaTeX build artifacts and OS noise
|
|
319
|
-
- Use `--verbose` to see detailed file operations (see [Global options](#global-options))
|
|
176
|
+
- Pulls remote changes, then pushes local changes
|
|
177
|
+
- Local modifications win if newer
|
|
178
|
+
- **Propagates local deletions** — use `--no-delete` to opt out
|
|
320
179
|
- Use `--dry-run` to preview without applying
|
|
321
180
|
|
|
322
181
|
#### How deletion propagation works
|
|
323
182
|
|
|
324
|
-
|
|
183
|
+
`olcli` records a manifest of remote files in `.olcli.json`. On next sync:
|
|
325
184
|
|
|
326
|
-
- File missing locally
|
|
185
|
+
- File missing locally + still on remote → deleted on Overleaf
|
|
327
186
|
- File new locally → uploaded
|
|
328
|
-
- File modified locally
|
|
187
|
+
- File modified locally → uploaded (local wins)
|
|
329
188
|
- File only on remote → downloaded
|
|
330
189
|
|
|
331
|
-
First-time syncs skip the deletion phase (no manifest
|
|
332
|
-
|
|
333
|
-
## Ignoring files
|
|
190
|
+
First-time syncs skip the deletion phase (no prior manifest to compare).
|
|
334
191
|
|
|
335
|
-
|
|
192
|
+
## Ignoring Files
|
|
336
193
|
|
|
337
194
|
### Three layers
|
|
338
195
|
|
|
339
|
-
| Layer |
|
|
196
|
+
| Layer | Source | Purpose |
|
|
340
197
|
|---|---|---|
|
|
341
|
-
| 1 |
|
|
342
|
-
| 2 | `.olignore` | Project-level patterns
|
|
343
|
-
| 3 | `.olignore.local` | Machine-specific patterns.
|
|
198
|
+
| 1 | Built-in | LaTeX intermediates, OS noise, build dirs. Always on. |
|
|
199
|
+
| 2 | `.olignore` | Project-level patterns (gitignore syntax). |
|
|
200
|
+
| 3 | `.olignore.local` | Machine-specific patterns. |
|
|
344
201
|
|
|
345
|
-
Later layers override earlier ones
|
|
202
|
+
Later layers override earlier ones. Negation (`!important.aux`) is supported.
|
|
346
203
|
|
|
347
204
|
### Special PDF rule
|
|
348
205
|
|
|
349
|
-
`X.pdf` is ignored only if
|
|
350
|
-
|
|
351
|
-
### Example `.olignore`
|
|
352
|
-
|
|
353
|
-
```gitignore
|
|
354
|
-
# Drafts that should never reach Overleaf
|
|
355
|
-
*.draft.tex
|
|
356
|
-
notes/
|
|
357
|
-
chapters/scratch/
|
|
358
|
-
|
|
359
|
-
# But keep this one auxiliary file
|
|
360
|
-
!important.aux
|
|
361
|
-
```
|
|
206
|
+
`X.pdf` is ignored only if `X.tex` (or `.ltx`) exists in the same folder.
|
|
362
207
|
|
|
363
208
|
### Inspecting and overriding
|
|
364
209
|
|
|
365
210
|
```bash
|
|
366
|
-
olcli ignored # list patterns
|
|
367
|
-
olcli push --show-ignored # see what was skipped
|
|
211
|
+
olcli ignored # list patterns in effect
|
|
212
|
+
olcli push --show-ignored # see what was skipped
|
|
368
213
|
olcli sync --no-default-ignore # only .olignore applies
|
|
369
|
-
olcli sync --no-ignore #
|
|
214
|
+
olcli sync --no-ignore # upload everything
|
|
370
215
|
```
|
|
371
216
|
|
|
372
217
|
## Configuration
|
|
373
218
|
|
|
374
|
-
Credentials are
|
|
219
|
+
Credentials are checked in order:
|
|
375
220
|
|
|
376
221
|
1. `OVERLEAF_SESSION` environment variable
|
|
377
222
|
2. `.olauth` file in current directory
|
|
378
|
-
3. Global config: `~/.config/olcli-nodejs/config.json`
|
|
379
|
-
|
|
380
|
-
### .olauth File
|
|
381
|
-
|
|
382
|
-
For project-specific credentials, create `.olauth` in your project directory:
|
|
223
|
+
3. Global config: `~/.config/olcli-nodejs/config.json`
|
|
383
224
|
|
|
384
|
-
|
|
385
|
-
s%3AyourSessionCookieValue...
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Self-hosted Overleaf / ShareLaTeX
|
|
389
|
-
|
|
390
|
-
You can point `olcli` at a self-hosted instance and override the session cookie name. Both flags are documented under [Global options](#global-options) and can be combined with any command.
|
|
391
|
-
|
|
392
|
-
```bash
|
|
393
|
-
olcli --base-url https://latex.example.org list
|
|
394
|
-
olcli --base-url https://latex.example.org --cookie-name overleaf.sid whoami
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
Persist these settings in `olcli` config so you don't have to repeat them:
|
|
225
|
+
### Self-hosted Overleaf
|
|
398
226
|
|
|
399
227
|
```bash
|
|
400
228
|
olcli config set-url https://latex.example.org
|
|
401
229
|
olcli config set-cookie-name overleaf.sid
|
|
402
230
|
```
|
|
403
231
|
|
|
404
|
-
|
|
232
|
+
Or pass per-command: `olcli --base-url https://latex.example.org list`
|
|
405
233
|
|
|
406
|
-
|
|
234
|
+
### Timeout
|
|
407
235
|
|
|
408
236
|
```bash
|
|
409
|
-
|
|
410
|
-
olcli --timeout 60000 pull "
|
|
411
|
-
|
|
412
|
-
# Persist as default
|
|
413
|
-
olcli config set-timeout 60000
|
|
414
|
-
|
|
415
|
-
# Or use environment variable
|
|
416
|
-
export OVERLEAF_TIMEOUT=60000
|
|
417
|
-
olcli pull "Large Thesis"
|
|
237
|
+
olcli config set-timeout 60000 # persist
|
|
238
|
+
olcli --timeout 60000 pull "Big Thesis" # one-off
|
|
239
|
+
export OVERLEAF_TIMEOUT=60000 # env var
|
|
418
240
|
```
|
|
419
241
|
|
|
420
|
-
Precedence: `--timeout`
|
|
242
|
+
Precedence: `--timeout` > `OVERLEAF_TIMEOUT` > config > default (10000ms).
|
|
421
243
|
|
|
422
244
|
## Examples
|
|
423
245
|
|
|
424
|
-
### Work on a thesis
|
|
425
|
-
|
|
426
246
|
```bash
|
|
427
|
-
#
|
|
428
|
-
olcli pull "PhD Thesis" thesis
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
# Daily workflow
|
|
432
|
-
vim chapters/introduction.tex
|
|
433
|
-
olcli sync
|
|
434
|
-
olcli pdf -o draft.pdf
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### Quick PDF download
|
|
247
|
+
# Daily thesis workflow
|
|
248
|
+
olcli pull "PhD Thesis" thesis && cd thesis
|
|
249
|
+
vim chapters/methods.tex
|
|
250
|
+
olcli sync && olcli pdf -o draft.pdf
|
|
438
251
|
|
|
439
|
-
|
|
252
|
+
# Quick PDF download
|
|
440
253
|
olcli pdf "Conference Paper" -o paper.pdf
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
### Download a single file
|
|
444
254
|
|
|
445
|
-
|
|
446
|
-
olcli download main.tex "My Project"
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
### Upload figures
|
|
450
|
-
|
|
451
|
-
```bash
|
|
452
|
-
cd my-project
|
|
255
|
+
# Upload figures
|
|
453
256
|
olcli upload figures/diagram.png
|
|
454
|
-
```
|
|
455
257
|
|
|
456
|
-
|
|
258
|
+
# arXiv submission prep
|
|
259
|
+
olcli output bbl -o main.bbl
|
|
260
|
+
olcli zip -o arxiv-submission.zip
|
|
457
261
|
|
|
458
|
-
|
|
262
|
+
# Backup all projects
|
|
459
263
|
for proj in $(olcli list --json | jq -r '.[].name'); do
|
|
460
264
|
olcli zip "$proj" -o "backups/${proj}.zip"
|
|
461
265
|
done
|
|
462
266
|
```
|
|
463
267
|
|
|
464
|
-
### Prepare for arXiv
|
|
465
|
-
|
|
466
|
-
```bash
|
|
467
|
-
cd my-paper
|
|
468
|
-
olcli output bbl -o main.bbl
|
|
469
|
-
olcli zip -o arxiv-submission.zip
|
|
470
|
-
```
|
|
471
|
-
|
|
472
268
|
## Programmatic Usage (Library API)
|
|
473
269
|
|
|
474
|
-
`@aloth/olcli` exposes `OverleafClient` and all public interfaces as a
|
|
270
|
+
`@aloth/olcli` exposes `OverleafClient` and all public interfaces as a library.
|
|
475
271
|
|
|
476
272
|
### Install
|
|
477
273
|
|
|
@@ -484,27 +280,15 @@ npm install @aloth/olcli
|
|
|
484
280
|
```ts
|
|
485
281
|
import { OverleafClient } from '@aloth/olcli';
|
|
486
282
|
|
|
487
|
-
// Create a client from an Overleaf session cookie
|
|
488
283
|
const client = await OverleafClient.fromSessionCookie(cookie);
|
|
489
284
|
|
|
490
|
-
// List all projects
|
|
491
285
|
const projects = await client.listProjects();
|
|
492
|
-
console.log(projects);
|
|
493
|
-
|
|
494
|
-
// Get detailed info (file tree) for a project
|
|
495
286
|
const info = await client.getProjectInfo(projectId);
|
|
496
|
-
|
|
497
|
-
// Download project as a zip buffer
|
|
498
287
|
const zipBuf = await client.downloadProject(projectId);
|
|
499
|
-
|
|
500
|
-
// Compile and download PDF
|
|
501
288
|
const pdfBuf = await client.downloadPdf(projectId);
|
|
502
289
|
|
|
503
|
-
// Upload a file
|
|
504
|
-
import { readFileSync } from 'node:fs';
|
|
505
290
|
await client.uploadFile(projectId, null, 'main.tex', readFileSync('main.tex'));
|
|
506
291
|
|
|
507
|
-
// List review comments
|
|
508
292
|
const comments = await client.listComments(projectId, { status: 'open' });
|
|
509
293
|
```
|
|
510
294
|
|
|
@@ -512,167 +296,33 @@ const comments = await client.listComments(projectId, { status: 'open' });
|
|
|
512
296
|
|
|
513
297
|
```ts
|
|
514
298
|
import {
|
|
515
|
-
// Core client
|
|
516
299
|
OverleafClient,
|
|
517
|
-
|
|
518
|
-
// Types / interfaces
|
|
300
|
+
// Types
|
|
519
301
|
Project, ProjectInfo, FolderEntry, DocEntry, FileEntry,
|
|
520
302
|
CommentMessage, ProjectComment, CommentContext, CommentStatus,
|
|
521
303
|
ListCommentsOptions, AddCommentOptions, Credentials, SessionCookiePair,
|
|
522
|
-
|
|
523
|
-
// Configuration utilities
|
|
304
|
+
// Config utilities
|
|
524
305
|
getBaseUrl, setBaseUrl, getSessionCookie, setSessionCookie,
|
|
525
306
|
getSessionCookieName, setSessionCookieName, getCsrf, setCsrf,
|
|
526
307
|
getLastProject, setLastProject, clearConfig, getConfigPath, saveOlAuth,
|
|
527
308
|
getTimeout, setTimeout, getPasswordCredentials, setPasswordCredentials,
|
|
528
309
|
clearPasswordCredentials, type PasswordCredentials,
|
|
529
|
-
|
|
530
310
|
// Ignore utilities
|
|
531
311
|
DEFAULT_IGNORE_PATTERNS, loadIgnore, shouldIgnore, buildTexSiblingSet,
|
|
532
312
|
IgnoreContext, LoadIgnoreOptions,
|
|
533
313
|
} from '@aloth/olcli';
|
|
534
314
|
```
|
|
535
315
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
## MCP Server
|
|
316
|
+
## Further Documentation
|
|
539
317
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
### MCP tools
|
|
543
|
-
|
|
544
|
-
| Tool | Description |
|
|
545
|
-
|------|-------------|
|
|
546
|
-
| `list_projects` | List all Overleaf projects |
|
|
547
|
-
| `get_project_info` | Get file tree and metadata for a project |
|
|
548
|
-
| `pull_project` | Download and extract a project to a local directory |
|
|
549
|
-
| `push_file` | Upload a local file to a project |
|
|
550
|
-
| `compile` | Compile a project and get the PDF URL |
|
|
551
|
-
| `download_pdf` | Compile a project and save the PDF locally |
|
|
552
|
-
| `list_comments` | List review comments (filter: all / open / resolved) |
|
|
553
|
-
| `get_entities` | Get a flat list of all files in a project |
|
|
554
|
-
| `download_file` | Download a specific file by its remote path |
|
|
555
|
-
| `add_comment` | Add a review comment to a document |
|
|
556
|
-
| `reply_to_comment` | Reply to an existing comment thread |
|
|
557
|
-
| `resolve_comment` | Mark a comment thread as resolved |
|
|
558
|
-
| `delete_entity` | Delete a file or document by path |
|
|
559
|
-
| `rename_entity` | Rename a file or document |
|
|
560
|
-
| `compile_with_outputs` | Compile and return all output files (PDF, BBL, logs…) |
|
|
561
|
-
|
|
562
|
-
### Authentication
|
|
563
|
-
|
|
564
|
-
The MCP server reads credentials in this order:
|
|
565
|
-
|
|
566
|
-
1. **`OVERLEAF_SESSION` environment variable** — set in your MCP config (recommended)
|
|
567
|
-
2. **`OVERLEAF_EMAIL` + `OVERLEAF_PASSWORD` environment variables** — for password login (self-hosted)
|
|
568
|
-
3. **`.olauth` file in cwd** — written by `olcli auth`
|
|
569
|
-
4. **Stored config** — written by `olcli auth` (including saved password credentials)
|
|
570
|
-
|
|
571
|
-
When a session cookie expires and password credentials are available, the MCP server automatically re-authenticates.
|
|
572
|
-
|
|
573
|
-
### Claude Desktop
|
|
574
|
-
|
|
575
|
-
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
576
|
-
|
|
577
|
-
```json
|
|
578
|
-
{
|
|
579
|
-
"mcpServers": {
|
|
580
|
-
"overleaf": {
|
|
581
|
-
"command": "npx",
|
|
582
|
-
"args": ["-y", "@aloth/olcli-mcp"],
|
|
583
|
-
"env": {
|
|
584
|
-
"OVERLEAF_SESSION": "<your-overleaf-session-cookie>"
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
Or if you have olcli installed globally (`npm install -g @aloth/olcli`):
|
|
592
|
-
|
|
593
|
-
```json
|
|
594
|
-
{
|
|
595
|
-
"mcpServers": {
|
|
596
|
-
"overleaf": {
|
|
597
|
-
"command": "olcli-mcp",
|
|
598
|
-
"env": {
|
|
599
|
-
"OVERLEAF_SESSION": "<your-overleaf-session-cookie>"
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
### Cursor
|
|
607
|
-
|
|
608
|
-
Add to your Cursor MCP settings (`~/.cursor/mcp.json` or project `.cursor/mcp.json`):
|
|
609
|
-
|
|
610
|
-
```json
|
|
611
|
-
{
|
|
612
|
-
"mcpServers": {
|
|
613
|
-
"overleaf": {
|
|
614
|
-
"command": "npx",
|
|
615
|
-
"args": ["-y", "@aloth/olcli-mcp"],
|
|
616
|
-
"env": {
|
|
617
|
-
"OVERLEAF_SESSION": "<your-overleaf-session-cookie>"
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
### Windsurf
|
|
625
|
-
|
|
626
|
-
Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
627
|
-
|
|
628
|
-
```json
|
|
629
|
-
{
|
|
630
|
-
"mcpServers": {
|
|
631
|
-
"overleaf": {
|
|
632
|
-
"command": "npx",
|
|
633
|
-
"args": ["-y", "@aloth/olcli-mcp"],
|
|
634
|
-
"env": {
|
|
635
|
-
"OVERLEAF_SESSION": "<your-overleaf-session-cookie>"
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
### Getting your session cookie
|
|
643
|
-
|
|
644
|
-
1. Open Overleaf in your browser and log in
|
|
645
|
-
2. Open DevTools → Application (Chrome) or Storage (Firefox) → Cookies
|
|
646
|
-
3. Find `overleaf_session2` (or `sharelatex.sid` for self-hosted)
|
|
647
|
-
4. Copy the value — that's your `OVERLEAF_SESSION`
|
|
648
|
-
|
|
649
|
-
Or run `olcli auth` and then the MCP server will pick it up automatically.
|
|
650
|
-
|
|
651
|
-
### Self-hosted Overleaf
|
|
652
|
-
|
|
653
|
-
Set `OVERLEAF_BASE_URL` in your MCP env:
|
|
654
|
-
|
|
655
|
-
```json
|
|
656
|
-
"env": {
|
|
657
|
-
"OVERLEAF_SESSION": "<cookie>",
|
|
658
|
-
"OVERLEAF_BASE_URL": "https://overleaf.yourcompany.com"
|
|
659
|
-
}
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
---
|
|
318
|
+
- [MCP Server](docs/MCP.md) — AI assistant integration (Claude, Cursor, Windsurf)
|
|
319
|
+
- [Git Remote Helper](docs/GIT-REMOTE.md) — use Overleaf as a native git remote
|
|
663
320
|
|
|
664
321
|
## Troubleshooting
|
|
665
322
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
If you get authentication errors, your session cookie may have expired. Get a fresh one from the browser and run `olcli auth` again.
|
|
669
|
-
|
|
670
|
-
### Compilation fails
|
|
323
|
+
**Session expired** — Get a fresh cookie from the browser and run `olcli auth` again.
|
|
671
324
|
|
|
672
|
-
Check the Overleaf web editor for detailed error logs
|
|
673
|
-
- Missing packages
|
|
674
|
-
- Syntax errors in `.tex` files
|
|
675
|
-
- Missing bibliography files
|
|
325
|
+
**Compilation fails** — Check the Overleaf web editor for detailed error logs (missing packages, syntax errors, missing bibliography files).
|
|
676
326
|
|
|
677
327
|
## Contributing
|
|
678
328
|
|
package/dist/mcp.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* git-remote-overleaf — Git remote helper for Overleaf projects.
|
|
4
|
+
*
|
|
5
|
+
* Allows native git commands against Overleaf:
|
|
6
|
+
* git clone overleaf::https://www.overleaf.com/project/<id>
|
|
7
|
+
* git pull
|
|
8
|
+
* git push
|
|
9
|
+
*
|
|
10
|
+
* Protocol reference: gitremote-helpers(7)
|
|
11
|
+
*
|
|
12
|
+
* Capabilities: import + export with refspec.
|
|
13
|
+
* State stored in .git/overleaf/ (manifest).
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=remote-helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-helper.d.ts","sourceRoot":"","sources":["../src/remote-helper.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* git-remote-overleaf — Git remote helper for Overleaf projects.
|
|
4
|
+
*
|
|
5
|
+
* Allows native git commands against Overleaf:
|
|
6
|
+
* git clone overleaf::https://www.overleaf.com/project/<id>
|
|
7
|
+
* git pull
|
|
8
|
+
* git push
|
|
9
|
+
*
|
|
10
|
+
* Protocol reference: gitremote-helpers(7)
|
|
11
|
+
*
|
|
12
|
+
* Capabilities: import + export with refspec.
|
|
13
|
+
* State stored in .git/overleaf/ (manifest).
|
|
14
|
+
*/
|
|
15
|
+
import { createInterface } from 'node:readline';
|
|
16
|
+
import { execSync } from 'node:child_process';
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import { join, resolve } from 'node:path';
|
|
19
|
+
import { OverleafClient } from './client.js';
|
|
20
|
+
import { getSessionCookie, getBaseUrl, getSessionCookieName, getTimeout, getPasswordCredentials, } from './config.js';
|
|
21
|
+
import { loadIgnore, shouldIgnore } from './ignore.js';
|
|
22
|
+
// ─── Logging (stderr only — stdout is the git protocol channel) ───
|
|
23
|
+
function debug(msg) {
|
|
24
|
+
if (process.env.GIT_REMOTE_OVERLEAF_DEBUG) {
|
|
25
|
+
process.stderr.write(`[git-remote-overleaf] ${msg}\n`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function fatal(msg) {
|
|
29
|
+
process.stderr.write(`fatal: ${msg}\n`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse an Overleaf project URL into base URL and project ID.
|
|
34
|
+
*
|
|
35
|
+
* Supported formats:
|
|
36
|
+
* https://www.overleaf.com/project/abc123
|
|
37
|
+
* overleaf::https://www.overleaf.com/project/abc123
|
|
38
|
+
* https://custom.overleaf.example.com/project/abc123
|
|
39
|
+
* abc123 (bare project ID, uses configured base URL)
|
|
40
|
+
*/
|
|
41
|
+
function parseRemoteUrl(url) {
|
|
42
|
+
// Strip the overleaf:: prefix if present (git passes the raw URL part)
|
|
43
|
+
const cleaned = url.replace(/^overleaf::/, '');
|
|
44
|
+
// Try parsing as URL with /project/<id> path
|
|
45
|
+
const projectPathMatch = cleaned.match(/^(https?:\/\/[^/]+)\/project\/([a-f0-9]{24})$/i);
|
|
46
|
+
if (projectPathMatch) {
|
|
47
|
+
return { baseUrl: projectPathMatch[1], projectId: projectPathMatch[2] };
|
|
48
|
+
}
|
|
49
|
+
// Bare 24-char hex ID
|
|
50
|
+
if (/^[a-f0-9]{24}$/i.test(cleaned)) {
|
|
51
|
+
return { baseUrl: getBaseUrl(), projectId: cleaned };
|
|
52
|
+
}
|
|
53
|
+
fatal(`Cannot parse Overleaf URL: ${url}\n Expected: https://<host>/project/<24-char-id>`);
|
|
54
|
+
}
|
|
55
|
+
function getGitDir() {
|
|
56
|
+
const gitDir = process.env.GIT_DIR;
|
|
57
|
+
if (!gitDir)
|
|
58
|
+
fatal('GIT_DIR not set — are you running inside git?');
|
|
59
|
+
return resolve(gitDir); // Always return absolute path
|
|
60
|
+
}
|
|
61
|
+
function stateDir() {
|
|
62
|
+
return join(getGitDir(), 'overleaf');
|
|
63
|
+
}
|
|
64
|
+
function ensureStateDir() {
|
|
65
|
+
const dir = stateDir();
|
|
66
|
+
if (!existsSync(dir))
|
|
67
|
+
mkdirSync(dir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
function loadState() {
|
|
70
|
+
const path = join(stateDir(), 'state.json');
|
|
71
|
+
if (!existsSync(path))
|
|
72
|
+
return { manifest: [], lastMark: 0 };
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return { manifest: [], lastMark: 0 };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function saveState(state) {
|
|
81
|
+
ensureStateDir();
|
|
82
|
+
writeFileSync(join(stateDir(), 'state.json'), JSON.stringify(state, null, 2));
|
|
83
|
+
}
|
|
84
|
+
// ─── Authentication (reuses olcli's config chain) ───
|
|
85
|
+
async function getClient(baseUrl) {
|
|
86
|
+
const cookieName = getSessionCookieName();
|
|
87
|
+
const cookie = getSessionCookie();
|
|
88
|
+
const passwordCredentials = getPasswordCredentials();
|
|
89
|
+
if (cookie) {
|
|
90
|
+
try {
|
|
91
|
+
const client = await OverleafClient.fromSessionCookie(cookie, baseUrl, cookieName);
|
|
92
|
+
client.setGlobalTimeout(getTimeout());
|
|
93
|
+
return client;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
if (!passwordCredentials) {
|
|
97
|
+
fatal(`Authentication failed: ${err.message}\nRun 'olcli login' first.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (passwordCredentials) {
|
|
102
|
+
const client = await OverleafClient.fromPasswordLogin(passwordCredentials.email, passwordCredentials.password, baseUrl);
|
|
103
|
+
client.setGlobalTimeout(getTimeout());
|
|
104
|
+
return client;
|
|
105
|
+
}
|
|
106
|
+
fatal('No Overleaf credentials found. Run \'olcli login\' or set OVERLEAF_SESSION.');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Read fast-export stream from stdin and extract file modifications/deletions.
|
|
110
|
+
*
|
|
111
|
+
* The stream comes after we respond to the "export" command. Git sends
|
|
112
|
+
* fast-export data terminated by "done\n".
|
|
113
|
+
*/
|
|
114
|
+
async function parseFastExport(rl) {
|
|
115
|
+
const files = [];
|
|
116
|
+
const deletes = [];
|
|
117
|
+
const blobData = new Map(); // mark → data
|
|
118
|
+
let currentMark = '';
|
|
119
|
+
let dataBuffer = Buffer.alloc(0);
|
|
120
|
+
// State machine for parsing the fast-export stream.
|
|
121
|
+
// readline gives us lines; for binary `data <N>` sections we accumulate bytes.
|
|
122
|
+
let state = 'command';
|
|
123
|
+
let pendingDataBytes = 0;
|
|
124
|
+
let pendingMark = '';
|
|
125
|
+
let commitSection = false;
|
|
126
|
+
for await (const line of rl) {
|
|
127
|
+
if (state === 'data') {
|
|
128
|
+
// Accumulate data — readline splits on \n so we rejoin
|
|
129
|
+
const lineBytes = Buffer.from(line + '\n', 'binary');
|
|
130
|
+
dataBuffer = Buffer.concat([dataBuffer, lineBytes]);
|
|
131
|
+
if (dataBuffer.length >= pendingDataBytes) {
|
|
132
|
+
// Trim to exact length
|
|
133
|
+
const finalData = dataBuffer.subarray(0, pendingDataBytes);
|
|
134
|
+
if (pendingMark) {
|
|
135
|
+
blobData.set(pendingMark, finalData);
|
|
136
|
+
}
|
|
137
|
+
state = 'command';
|
|
138
|
+
dataBuffer = Buffer.alloc(0);
|
|
139
|
+
pendingMark = '';
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// Command mode
|
|
144
|
+
if (line === 'done' || line === '') {
|
|
145
|
+
if (line === 'done')
|
|
146
|
+
break;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (line.startsWith('blob')) {
|
|
150
|
+
commitSection = false;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (line.startsWith('mark :')) {
|
|
154
|
+
currentMark = line.slice(6);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (line.startsWith('data ')) {
|
|
158
|
+
pendingDataBytes = parseInt(line.slice(5), 10);
|
|
159
|
+
pendingMark = commitSection ? '' : currentMark;
|
|
160
|
+
state = 'data';
|
|
161
|
+
dataBuffer = Buffer.alloc(0);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (line.startsWith('commit ')) {
|
|
165
|
+
commitSection = true;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (line.startsWith('committer ') || line.startsWith('author ')) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (line.startsWith('from ') || line.startsWith('merge ')) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
// File modification: M <mode> <dataref> <path>
|
|
175
|
+
const mMatch = line.match(/^M \d+ :(\S+) (.+)$/);
|
|
176
|
+
if (mMatch) {
|
|
177
|
+
const [, markRef, path] = mMatch;
|
|
178
|
+
const content = blobData.get(markRef);
|
|
179
|
+
if (content) {
|
|
180
|
+
files.push({ path, content });
|
|
181
|
+
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
// Inline modification: M <mode> inline <path>
|
|
185
|
+
const mInline = line.match(/^M \d+ inline (.+)$/);
|
|
186
|
+
if (mInline) {
|
|
187
|
+
// Next will be a data line
|
|
188
|
+
currentMark = `__inline_${mInline[1]}`;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
// Deletion: D <path>
|
|
192
|
+
const dMatch = line.match(/^D (.+)$/);
|
|
193
|
+
if (dMatch) {
|
|
194
|
+
deletes.push({ path: dMatch[1] });
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Resolve any inline blobs
|
|
199
|
+
for (const [mark, data] of blobData) {
|
|
200
|
+
if (mark.startsWith('__inline_')) {
|
|
201
|
+
const path = mark.slice(9);
|
|
202
|
+
files.push({ path, content: data });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return { files, deletes };
|
|
206
|
+
}
|
|
207
|
+
// ─── Push logic ───
|
|
208
|
+
async function pushChanges(client, projectId, parsed, _state) {
|
|
209
|
+
const ignoreCtx = loadIgnore(process.cwd(), {});
|
|
210
|
+
// Upload modified/new files
|
|
211
|
+
for (const file of parsed.files) {
|
|
212
|
+
if (shouldIgnore(file.path, ignoreCtx)) {
|
|
213
|
+
debug(`Skipping ignored file: ${file.path}`);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
debug(`Uploading: ${file.path}`);
|
|
217
|
+
await client.uploadFile(projectId, null, file.path, file.content);
|
|
218
|
+
}
|
|
219
|
+
// Delete removed files
|
|
220
|
+
for (const del of parsed.deletes) {
|
|
221
|
+
debug(`Deleting: ${del.path}`);
|
|
222
|
+
try {
|
|
223
|
+
await client.deleteByPath(projectId, del.path);
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
// File might already be gone on remote
|
|
227
|
+
debug(`Warning: Could not delete ${del.path}: ${err.message}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// ─── Protocol handler ───
|
|
232
|
+
async function main() {
|
|
233
|
+
// Args: git-remote-overleaf <remote-name> <url>
|
|
234
|
+
const args = process.argv.slice(2);
|
|
235
|
+
if (args.length < 1) {
|
|
236
|
+
fatal('Usage: git-remote-overleaf <remote> [<url>]');
|
|
237
|
+
}
|
|
238
|
+
// The URL is usually the second arg; if only one arg, it IS the URL
|
|
239
|
+
const rawUrl = args[1] || args[0];
|
|
240
|
+
const { projectId, baseUrl } = parseRemoteUrl(rawUrl);
|
|
241
|
+
debug(`Project ID: ${projectId}, Base URL: ${baseUrl}`);
|
|
242
|
+
ensureStateDir();
|
|
243
|
+
let state = loadState();
|
|
244
|
+
const rl = createInterface({
|
|
245
|
+
input: process.stdin,
|
|
246
|
+
terminal: false,
|
|
247
|
+
});
|
|
248
|
+
const iterator = rl[Symbol.asyncIterator]();
|
|
249
|
+
for await (const line of rl) {
|
|
250
|
+
const trimmed = line.trim();
|
|
251
|
+
debug(`< ${trimmed}`);
|
|
252
|
+
if (trimmed === '') {
|
|
253
|
+
// Empty line = end of command batch (for list/import/export batches)
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (trimmed === 'capabilities') {
|
|
257
|
+
// Declare what we support.
|
|
258
|
+
ensureStateDir();
|
|
259
|
+
// Create empty marks file if it doesn't exist (fast-export --import-marks
|
|
260
|
+
// fails if file is missing; empty file = nothing previously exported).
|
|
261
|
+
const marks = join(stateDir(), 'marks');
|
|
262
|
+
if (!existsSync(marks)) {
|
|
263
|
+
writeFileSync(marks, '');
|
|
264
|
+
}
|
|
265
|
+
const caps = [
|
|
266
|
+
'import',
|
|
267
|
+
'export',
|
|
268
|
+
// Map remote refs/heads/* to a private namespace refs/overleaf/<remote>/*
|
|
269
|
+
// This is critical: git uses the private namespace to track what the remote has.
|
|
270
|
+
// Without a separate namespace, git can't compute the push delta.
|
|
271
|
+
'refspec refs/heads/*:refs/overleaf/*',
|
|
272
|
+
// import-marks/export-marks = passed to fast-export on push.
|
|
273
|
+
`import-marks ${marks}`,
|
|
274
|
+
`export-marks ${marks}`,
|
|
275
|
+
'', // blank line terminates capabilities
|
|
276
|
+
].join('\n');
|
|
277
|
+
process.stdout.write(caps + '\n');
|
|
278
|
+
debug(`> capabilities response sent (marks=${marks})`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (trimmed === 'list' || trimmed === 'list for-push') {
|
|
282
|
+
// Report what SHA the remote currently has.
|
|
283
|
+
// Read from refs/overleaf/main (our private namespace ref updated by
|
|
284
|
+
// git after import/export via the refspec mapping).
|
|
285
|
+
let remoteSha;
|
|
286
|
+
try {
|
|
287
|
+
const sha = execSync('git rev-parse --verify refs/overleaf/main 2>/dev/null', { encoding: 'utf-8', env: { ...process.env, GIT_DIR: getGitDir() } }).trim();
|
|
288
|
+
if (/^[a-f0-9]{40}$/.test(sha))
|
|
289
|
+
remoteSha = sha;
|
|
290
|
+
}
|
|
291
|
+
catch { /* not yet created */ }
|
|
292
|
+
if (remoteSha) {
|
|
293
|
+
process.stdout.write(`${remoteSha} refs/heads/main\n`);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
process.stdout.write('? refs/heads/main\n');
|
|
297
|
+
}
|
|
298
|
+
process.stdout.write('@refs/heads/main HEAD\n');
|
|
299
|
+
process.stdout.write('\n'); // blank line terminates
|
|
300
|
+
debug(`> list response sent (sha=${remoteSha || '?'})`);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (trimmed.startsWith('import ')) {
|
|
304
|
+
// import refs/heads/main
|
|
305
|
+
// May be followed by more "import" lines, terminated by blank line
|
|
306
|
+
const refs = [trimmed.slice(7)];
|
|
307
|
+
// Consume additional import lines until blank
|
|
308
|
+
for await (const nextLine of rl) {
|
|
309
|
+
const next = nextLine.trim();
|
|
310
|
+
if (next === '')
|
|
311
|
+
break;
|
|
312
|
+
if (next.startsWith('import ')) {
|
|
313
|
+
refs.push(next.slice(7));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
debug(`Import requested for: ${refs.join(', ')}`);
|
|
317
|
+
// Authenticate and fetch
|
|
318
|
+
const client = await getClient(baseUrl);
|
|
319
|
+
debug('Generating fast-import stream...');
|
|
320
|
+
const zipBuffer = await client.downloadProject(projectId);
|
|
321
|
+
const AdmZip = (await import('adm-zip')).default;
|
|
322
|
+
const zip = new AdmZip(zipBuffer);
|
|
323
|
+
const entries = zip.getEntries();
|
|
324
|
+
let mark = state.lastMark;
|
|
325
|
+
const newManifest = [];
|
|
326
|
+
const fileMarks = [];
|
|
327
|
+
// Emit feature lines so fast-import writes/reads marks.
|
|
328
|
+
// This enables incremental import and allows fast-export to know
|
|
329
|
+
// which objects were already imported.
|
|
330
|
+
const marks = join(stateDir(), 'marks');
|
|
331
|
+
process.stdout.write(`feature import-marks-if-exists=${marks}\n`);
|
|
332
|
+
process.stdout.write(`feature export-marks=${marks}\n`);
|
|
333
|
+
// feature done tells fast-import to expect "done" as terminator
|
|
334
|
+
process.stdout.write('feature done\n');
|
|
335
|
+
// Write blobs
|
|
336
|
+
for (const entry of entries) {
|
|
337
|
+
if (entry.isDirectory)
|
|
338
|
+
continue;
|
|
339
|
+
const path = entry.entryName;
|
|
340
|
+
newManifest.push(path);
|
|
341
|
+
mark++;
|
|
342
|
+
const data = entry.getData();
|
|
343
|
+
process.stdout.write(`blob\nmark :${mark}\ndata ${data.length}\n`);
|
|
344
|
+
process.stdout.write(data);
|
|
345
|
+
process.stdout.write('\n');
|
|
346
|
+
fileMarks.push({ path, mark });
|
|
347
|
+
}
|
|
348
|
+
// Write commit
|
|
349
|
+
mark++;
|
|
350
|
+
const now = Math.floor(Date.now() / 1000);
|
|
351
|
+
// Write to private namespace per refspec: refs/heads/* → refs/overleaf/*
|
|
352
|
+
process.stdout.write(`commit refs/overleaf/main\n`);
|
|
353
|
+
process.stdout.write(`mark :${mark}\n`);
|
|
354
|
+
process.stdout.write(`committer Overleaf <overleaf@localhost> ${now} +0000\n`);
|
|
355
|
+
const msg = 'Import from Overleaf';
|
|
356
|
+
process.stdout.write(`data ${Buffer.byteLength(msg)}\n`);
|
|
357
|
+
process.stdout.write(`${msg}\n`);
|
|
358
|
+
// Parent: reference previous state if any
|
|
359
|
+
if (state.manifest.length > 0) {
|
|
360
|
+
process.stdout.write(`from refs/overleaf/main^0\n`);
|
|
361
|
+
}
|
|
362
|
+
// Deletions (files removed on Overleaf since last import)
|
|
363
|
+
const removedFiles = state.manifest.filter(f => !newManifest.includes(f));
|
|
364
|
+
for (const f of removedFiles) {
|
|
365
|
+
process.stdout.write(`D ${f}\n`);
|
|
366
|
+
}
|
|
367
|
+
// Modifications (all current files)
|
|
368
|
+
for (const fm of fileMarks) {
|
|
369
|
+
process.stdout.write(`M 100644 :${fm.mark} ${fm.path}\n`);
|
|
370
|
+
}
|
|
371
|
+
process.stdout.write('\n'); // end of commit
|
|
372
|
+
process.stdout.write('done\n');
|
|
373
|
+
// Update state
|
|
374
|
+
state.manifest = newManifest;
|
|
375
|
+
state.lastMark = mark;
|
|
376
|
+
saveState(state);
|
|
377
|
+
debug(`Import complete: ${fileMarks.length} files, ${removedFiles.length} deletions`);
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (trimmed.startsWith('export')) {
|
|
381
|
+
// Push: git sends a fast-export stream on stdin
|
|
382
|
+
debug('Export (push) starting — reading fast-export stream...');
|
|
383
|
+
const client = await getClient(baseUrl);
|
|
384
|
+
const parsed = await parseFastExport(iterator);
|
|
385
|
+
debug(`Parsed export: ${parsed.files.length} modifications, ${parsed.deletes.length} deletions`);
|
|
386
|
+
if (parsed.files.length === 0 && parsed.deletes.length === 0) {
|
|
387
|
+
debug('Nothing to push.');
|
|
388
|
+
// Don't report ok — nothing was actually pushed.
|
|
389
|
+
// This prevents git from advancing the remote tracking ref.
|
|
390
|
+
process.stdout.write('\n');
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
await pushChanges(client, projectId, parsed, state);
|
|
394
|
+
// Report success for each ref that was pushed
|
|
395
|
+
process.stdout.write('ok refs/heads/main\n');
|
|
396
|
+
process.stdout.write('\n');
|
|
397
|
+
// Update state with new manifest
|
|
398
|
+
const currentManifest = new Set(state.manifest);
|
|
399
|
+
for (const del of parsed.deletes) {
|
|
400
|
+
currentManifest.delete(del.path);
|
|
401
|
+
}
|
|
402
|
+
for (const file of parsed.files) {
|
|
403
|
+
currentManifest.add(file.path);
|
|
404
|
+
}
|
|
405
|
+
state.manifest = [...currentManifest];
|
|
406
|
+
saveState(state);
|
|
407
|
+
}
|
|
408
|
+
debug('Export complete.');
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (trimmed.startsWith('option ')) {
|
|
412
|
+
// We don't support any options currently
|
|
413
|
+
process.stdout.write('unsupported\n');
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
// Unknown command
|
|
417
|
+
debug(`Unknown command: ${trimmed}`);
|
|
418
|
+
fatal(`Unsupported command: ${trimmed}`);
|
|
419
|
+
}
|
|
420
|
+
rl.close();
|
|
421
|
+
}
|
|
422
|
+
// Gracefully handle EPIPE (git closed the pipe before we finished writing)
|
|
423
|
+
process.stdout.on('error', (err) => {
|
|
424
|
+
if (err.code === 'EPIPE')
|
|
425
|
+
process.exit(0);
|
|
426
|
+
throw err;
|
|
427
|
+
});
|
|
428
|
+
main().catch((err) => {
|
|
429
|
+
fatal(err.message || String(err));
|
|
430
|
+
});
|
|
431
|
+
//# sourceMappingURL=remote-helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-helper.js","sourceRoot":"","sources":["../src/remote-helper.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aloth/olcli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Command-line interface for Overleaf — Sync, manage, and compile LaTeX projects from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
},
|
|
15
15
|
"bin": {
|
|
16
16
|
"olcli": "dist/cli.js",
|
|
17
|
-
"olcli-mcp": "dist/mcp.js"
|
|
17
|
+
"olcli-mcp": "dist/mcp.js",
|
|
18
|
+
"git-remote-overleaf": "dist/remote-helper.js"
|
|
18
19
|
},
|
|
19
20
|
"scripts": {
|
|
20
21
|
"build": "tsc",
|