leafrelay 0.2.7 → 0.2.9
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 +30 -4
- package/README.zh-CN.md +30 -4
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +45 -48
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +1 -0
- package/dist/daemon.js +866 -933
- package/dist/daemon.js.map +1 -1
- package/dist/dist-JyfvbgjD.js +2923 -0
- package/dist/dist-JyfvbgjD.js.map +1 -0
- package/dist/index.d.ts +15 -16
- package/dist/index.js +1 -7
- package/dist/serve-CQnCOCAM.js +6823 -0
- package/dist/serve-CQnCOCAM.js.map +1 -0
- package/package.json +10 -10
- package/dist/chunk-3TTRDNHP.js +0 -3333
- package/dist/chunk-3TTRDNHP.js.map +0 -1
- package/dist/chunk-VXZVIMCG.js +0 -5305
- package/dist/chunk-VXZVIMCG.js.map +0 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# LeafRelay CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Two-way Overleaf sync from your terminal.** Edit LaTeX with your preferred editor, scripts, or AI tools while collaborators keep working in Overleaf.
|
|
4
|
+
|
|
5
|
+
[中文](https://github.com/yunhaoli24/leafrelay/blob/main/packages/cli/README.zh-CN.md) · [VS Code extension and screenshots](https://github.com/yunhaoli24/leafrelay#readme) · [Issues](https://github.com/yunhaoli24/leafrelay/issues)
|
|
6
|
+
|
|
7
|
+
## Why use it?
|
|
8
|
+
|
|
9
|
+
- External file changes trigger uploads without an extra IDE save.
|
|
10
|
+
- Checkpoints support incremental startup and reconnects.
|
|
11
|
+
- Three-way merging combines non-overlapping edits; conflicting paths pause for review.
|
|
12
|
+
- Multiple CLI processes and VS Code windows share one daemon, request queue, and sync engine per local folder.
|
|
13
|
+
- Per-file logs show what changed; account-wide request scheduling respects Overleaf rate limits.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
Requires **Node.js 24 or later** and a local replica containing `.overleaf/settings.json`. Set up the directory once with the VS Code extension's **Open Project Locally…** command; `serve` reuses that association and does not create one.
|
|
4
18
|
|
|
5
19
|
```bash
|
|
6
20
|
npm install --global leafrelay
|
|
@@ -9,8 +23,20 @@ cd /path/to/local-replica
|
|
|
9
23
|
leafrelay serve
|
|
10
24
|
```
|
|
11
25
|
|
|
12
|
-
`
|
|
26
|
+
`login` prompts for a browser cookie or email/password. Login sessions are saved per server in `~/.leafrelay/config.json` and shared with the VS Code extension. For a self-hosted server, pass its URL to `login`.
|
|
27
|
+
|
|
28
|
+
Keep `serve` running to keep the folder synchronized. You can pass a folder directly with `leafrelay serve /path/to/project`. Press **Ctrl+C** to detach; synchronization stops when that folder has no remaining clients. To review or resolve paused conflicts, open the same folder with the VS Code extension.
|
|
29
|
+
|
|
30
|
+
## Share the backend with VS Code
|
|
31
|
+
|
|
32
|
+
Running the CLI and extension together on the same folder does not create duplicate uploads. One Overleaf project may have one writable local directory; different projects can run in parallel. Keep both clients on the same release version: an older daemon restarts automatically, while a newer daemon requires upgrading the client.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
leafrelay daemon status
|
|
36
|
+
leafrelay daemon stop
|
|
37
|
+
leafrelay daemon restart
|
|
38
|
+
```
|
|
13
39
|
|
|
14
|
-
|
|
40
|
+
`LEAFRELAY_HOME` relocates all user state, `LEAFRELAY_CONFIG` selects another login file, and `LEAFRELAY_COOKIE` overrides the saved cookie for the current process. Dot-prefixed paths and symbolic links are excluded from synchronization.
|
|
15
41
|
|
|
16
|
-
For the
|
|
42
|
+
For the library's `startServe()` API, configuration, and architecture, see the [documentation](https://github.com/yunhaoli24/leafrelay/blob/main/docs/anatomy.md). LeafRelay is an independently maintained fork of [Overleaf Workshop](https://github.com/overleaf-workshop/Overleaf-Workshop), licensed under AGPL-3.0-only.
|
package/README.zh-CN.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# LeafRelay CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**在终端中双向同步 Overleaf。** 你用熟悉的编辑器、脚本或 AI 工具修改 LaTeX,合作者继续在 Overleaf 网页端写作。
|
|
4
|
+
|
|
5
|
+
[English](https://github.com/yunhaoli24/leafrelay/blob/main/packages/cli/README.md) · [VS Code 扩展与截图](https://github.com/yunhaoli24/leafrelay#readme) · [问题反馈](https://github.com/yunhaoli24/leafrelay/issues)
|
|
6
|
+
|
|
7
|
+
## 有什么优势?
|
|
8
|
+
|
|
9
|
+
- 外部工具修改文件后自动上传,不必再去 IDE 保存一次。
|
|
10
|
+
- 使用检查点增量启动和重连,减少重复下载。
|
|
11
|
+
- 三方合并自动处理不重叠的文本修改,冲突路径暂停等待处理。
|
|
12
|
+
- 多个 CLI 进程与 VS Code 窗口共享 daemon,每个本地目录只使用一套请求队列与同步引擎。
|
|
13
|
+
- 文件日志显示修改情况;统一调度同一账号的请求,遵守 Overleaf 限流。
|
|
14
|
+
|
|
15
|
+
## 快速上手
|
|
16
|
+
|
|
17
|
+
需要 **Node.js 24 或更高版本**,以及包含 `.overleaf/settings.json` 的本地副本。先用 VS Code 扩展的 **Open Project Locally…** 配置一次目录;`serve` 会复用这份关联,不会创建新的项目关联。
|
|
4
18
|
|
|
5
19
|
```bash
|
|
6
20
|
npm install --global leafrelay
|
|
@@ -9,8 +23,20 @@ cd /path/to/local-replica
|
|
|
9
23
|
leafrelay serve
|
|
10
24
|
```
|
|
11
25
|
|
|
12
|
-
`
|
|
26
|
+
`login` 会提示选择浏览器 Cookie 或邮箱和密码。登录状态按服务器保存到 `~/.leafrelay/config.json`,与 VS Code 扩展共用。自托管服务器可以直接把自己的 URL 传给 `login`。
|
|
27
|
+
|
|
28
|
+
保持 `serve` 运行即可持续同步,也可以用 `leafrelay serve /path/to/project` 直接指定目录。按 **Ctrl+C** 退出当前客户端;该目录没有其他客户端时会停止同步。需要查看或解决暂停的冲突时,用 VS Code 扩展打开同一目录即可。
|
|
29
|
+
|
|
30
|
+
## 与 VS Code 共用后端
|
|
31
|
+
|
|
32
|
+
CLI 和扩展同时打开相同目录,不会重复上传。每个 Overleaf 项目只对应一个可写本地目录,不同项目可以并行运行。两个客户端请保持相同发布版本:旧 daemon 会自动重启升级,daemon 版本更高时则要求先升级客户端。
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
leafrelay daemon status
|
|
36
|
+
leafrelay daemon stop
|
|
37
|
+
leafrelay daemon restart
|
|
38
|
+
```
|
|
13
39
|
|
|
14
|
-
|
|
40
|
+
`LEAFRELAY_HOME` 可以移动全部用户状态,`LEAFRELAY_CONFIG` 指定其他登录文件,`LEAFRELAY_COOKIE` 只覆盖当前进程使用的 Cookie。点号开头的路径与软链接不会参与同步。
|
|
15
41
|
|
|
16
|
-
|
|
42
|
+
库接口 `startServe()`、配置及架构说明见[文档](https://github.com/yunhaoli24/leafrelay/blob/main/docs/anatomy.md)。LeafRelay 是独立维护的 [Overleaf Workshop](https://github.com/overleaf-workshop/Overleaf-Workshop) 分支,采用 AGPL-3.0-only 许可证。
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.js
CHANGED
|
@@ -1,62 +1,59 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
daemonRestart,
|
|
5
|
-
daemonStatus,
|
|
6
|
-
daemonStop,
|
|
7
|
-
serve
|
|
8
|
-
} from "./chunk-VXZVIMCG.js";
|
|
9
|
-
import {
|
|
10
|
-
configPath,
|
|
11
|
-
normalizeServerUrl
|
|
12
|
-
} from "./chunk-3TTRDNHP.js";
|
|
13
|
-
|
|
14
|
-
// src/cli.ts
|
|
2
|
+
import { a as configPath, s as normalizeServerUrl } from "./dist-JyfvbgjD.js";
|
|
3
|
+
import { i as serve, n as daemonStatus, o as connectCliDaemon, r as daemonStop, t as daemonRestart } from "./serve-CQnCOCAM.js";
|
|
15
4
|
import { Command } from "commander";
|
|
16
|
-
|
|
17
|
-
// src/login.ts
|
|
18
5
|
import { input, password, select } from "@inquirer/prompts";
|
|
6
|
+
// src/login.ts
|
|
19
7
|
async function login(server, options) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
8
|
+
const url = normalizeServerUrl(server);
|
|
9
|
+
const environmentCookie = process.env.LEAFRELAY_COOKIE;
|
|
10
|
+
let cookie = options.cookie ?? environmentCookie;
|
|
11
|
+
let email = options.email;
|
|
12
|
+
let secret = options.password;
|
|
13
|
+
if (!cookie && !email) {
|
|
14
|
+
if (await select({
|
|
15
|
+
message: `Log in to ${url}`,
|
|
16
|
+
choices: [{
|
|
17
|
+
name: "Browser cookie",
|
|
18
|
+
value: "cookie"
|
|
19
|
+
}, {
|
|
20
|
+
name: "Email and password",
|
|
21
|
+
value: "password"
|
|
22
|
+
}]
|
|
23
|
+
}) === "cookie") cookie = await password({ message: "Cookie" });
|
|
24
|
+
else {
|
|
25
|
+
email = await input({ message: "Email" });
|
|
26
|
+
secret = await password({ message: "Password" });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const client = await connectCliDaemon();
|
|
30
|
+
try {
|
|
31
|
+
const user = await client.login(cookie ? {
|
|
32
|
+
server: url,
|
|
33
|
+
cookie
|
|
34
|
+
} : {
|
|
35
|
+
server: url,
|
|
36
|
+
email: email ?? "",
|
|
37
|
+
password: secret ?? await password({ message: "Password" })
|
|
38
|
+
});
|
|
39
|
+
console.log(`Logged in to ${url} as ${user.userEmail || email || user.userId}.`);
|
|
40
|
+
console.log(`Session saved to ${configPath()}.`);
|
|
41
|
+
} finally {
|
|
42
|
+
client.close();
|
|
43
|
+
}
|
|
48
44
|
}
|
|
49
|
-
|
|
50
45
|
// src/cli.ts
|
|
51
|
-
|
|
46
|
+
const program = new Command().name("leafrelay").description("Keep a local directory synchronized with an Overleaf project.").version("0.2.9");
|
|
52
47
|
program.command("login").description("Authenticate to Overleaf and save the session in ~/.leafrelay/config.json.").argument("[server]", "Overleaf server URL", "https://www.overleaf.com/").option("--cookie <cookie>", "Overleaf browser cookie").option("--email <email>", "Overleaf account email").option("--password <password>", "Overleaf account password").action(login);
|
|
53
48
|
program.command("serve").description("Synchronize the current directory with its configured Overleaf project.").argument("[directory]", "Local replica directory", process.cwd()).action(serve);
|
|
54
|
-
|
|
49
|
+
const daemon = program.command("daemon").description("Inspect or control the shared LeafRelay daemon.");
|
|
55
50
|
daemon.command("status").description("Show daemon clients and replicas.").action(daemonStatus);
|
|
56
51
|
daemon.command("stop").description("Stop the daemon and its active replicas.").action(daemonStop);
|
|
57
52
|
daemon.command("restart").description("Restart the daemon.").action(daemonRestart);
|
|
58
53
|
program.parseAsync().catch((error) => {
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
55
|
+
process.exitCode = 1;
|
|
61
56
|
});
|
|
57
|
+
export {};
|
|
58
|
+
|
|
62
59
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/login.ts","../src/cli.ts"],"sourcesContent":["import { input, password, select } from '@inquirer/prompts';\nimport { configPath, normalizeServerUrl } from '@leafrelay/core';\nimport { connectCliDaemon } from './daemonClient';\n\nexport interface LoginOptions {\n cookie?: string;\n email?: string;\n password?: string;\n}\n\nexport async function login(server: string, options: LoginOptions): Promise<void> {\n const url = normalizeServerUrl(server);\n const environmentCookie = process.env.LEAFRELAY_COOKIE;\n let cookie = options.cookie ?? environmentCookie;\n let email = options.email;\n let secret = options.password;\n\n if (!cookie && !email) {\n const method = await select({\n message: `Log in to ${url}`,\n choices: [\n { name: 'Browser cookie', value: 'cookie' },\n { name: 'Email and password', value: 'password' },\n ],\n });\n if (method === 'cookie') {\n cookie = await password({ message: 'Cookie' });\n } else {\n email = await input({ message: 'Email' });\n secret = await password({ message: 'Password' });\n }\n }\n\n const client = await connectCliDaemon();\n try {\n const user = await client.login(\n cookie\n ? { server: url, cookie }\n : { server: url, email: email ?? '', password: secret ?? (await password({ message: 'Password' })) },\n );\n console.log(`Logged in to ${url} as ${user.userEmail || email || user.userId}.`);\n console.log(`Session saved to ${configPath()}.`);\n } finally {\n client.close();\n }\n}\n","#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { login } from './login';\nimport { daemonRestart, daemonStatus, daemonStop, serve } from './serve';\n\ndeclare const LEAFRELAY_VERSION: string;\n\nconst program = new Command()\n .name('leafrelay')\n .description('Keep a local directory synchronized with an Overleaf project.')\n .version(LEAFRELAY_VERSION);\n\nprogram\n .command('login')\n .description('Authenticate to Overleaf and save the session in ~/.leafrelay/config.json.')\n .argument('[server]', 'Overleaf server URL', 'https://www.overleaf.com/')\n .option('--cookie <cookie>', 'Overleaf browser cookie')\n .option('--email <email>', 'Overleaf account email')\n .option('--password <password>', 'Overleaf account password')\n .action(login);\n\nprogram\n .command('serve')\n .description('Synchronize the current directory with its configured Overleaf project.')\n .argument('[directory]', 'Local replica directory', process.cwd())\n .action(serve);\n\nconst daemon = program.command('daemon').description('Inspect or control the shared LeafRelay daemon.');\n\ndaemon.command('status').description('Show daemon clients and replicas.').action(daemonStatus);\ndaemon.command('stop').description('Stop the daemon and its active replicas.').action(daemonStop);\ndaemon.command('restart').description('Restart the daemon.').action(daemonRestart);\n\nprogram.parseAsync().catch((error) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;AAUA,eAAsB,MAAM,QAAgB,SAAsC;CAC9E,MAAM,MAAM,mBAAmB,MAAM;CACrC,MAAM,oBAAoB,QAAQ,IAAI;CACtC,IAAI,SAAS,QAAQ,UAAU;CAC/B,IAAI,QAAQ,QAAQ;CACpB,IAAI,SAAS,QAAQ;CAErB,IAAI,CAAC,UAAU,CAAC,OAAO;EAQnB,IAAI,MAPiB,OAAO;GACxB,SAAS,aAAa;GACtB,SAAS,CACL;IAAE,MAAM;IAAkB,OAAO;GAAS,GAC1C;IAAE,MAAM;IAAsB,OAAO;GAAW,CACpD;EACJ,CAAC,MACc,UACX,SAAS,MAAM,SAAS,EAAE,SAAS,SAAS,CAAC;OAC1C;GACH,QAAQ,MAAM,MAAM,EAAE,SAAS,QAAQ,CAAC;GACxC,SAAS,MAAM,SAAS,EAAE,SAAS,WAAW,CAAC;EACnD;CACJ;CAEA,MAAM,SAAS,MAAM,iBAAiB;CACtC,IAAI;EACA,MAAM,OAAO,MAAM,OAAO,MACtB,SACM;GAAE,QAAQ;GAAK;EAAO,IACtB;GAAE,QAAQ;GAAK,OAAO,SAAS;GAAI,UAAU,UAAW,MAAM,SAAS,EAAE,SAAS,WAAW,CAAC;EAAG,CAC3G;EACA,QAAQ,IAAI,gBAAgB,IAAI,MAAM,KAAK,aAAa,SAAS,KAAK,OAAO,EAAE;EAC/E,QAAQ,IAAI,oBAAoB,WAAW,EAAE,EAAE;CACnD,UAAU;EACN,OAAO,MAAM;CACjB;AACJ;;ACtCA,MAAM,UAAU,IAAI,QAAQ,CAAC,CACxB,KAAK,WAAW,CAAC,CACjB,YAAY,+DAA+D,CAAC,CAC5E,QAAA,OAAyB;AAE9B,QACK,QAAQ,OAAO,CAAC,CAChB,YAAY,4EAA4E,CAAC,CACzF,SAAS,YAAY,uBAAuB,2BAA2B,CAAC,CACxE,OAAO,qBAAqB,yBAAyB,CAAC,CACtD,OAAO,mBAAmB,wBAAwB,CAAC,CACnD,OAAO,yBAAyB,2BAA2B,CAAC,CAC5D,OAAO,KAAK;AAEjB,QACK,QAAQ,OAAO,CAAC,CAChB,YAAY,yEAAyE,CAAC,CACtF,SAAS,eAAe,2BAA2B,QAAQ,IAAI,CAAC,CAAC,CACjE,OAAO,KAAK;AAEjB,MAAM,SAAS,QAAQ,QAAQ,QAAQ,CAAC,CAAC,YAAY,iDAAiD;AAEtG,OAAO,QAAQ,QAAQ,CAAC,CAAC,YAAY,mCAAmC,CAAC,CAAC,OAAO,YAAY;AAC7F,OAAO,QAAQ,MAAM,CAAC,CAAC,YAAY,0CAA0C,CAAC,CAAC,OAAO,UAAU;AAChG,OAAO,QAAQ,SAAS,CAAC,CAAC,YAAY,qBAAqB,CAAC,CAAC,OAAO,aAAa;AAEjF,QAAQ,WAAW,CAAC,CAAC,OAAO,UAAU;CAClC,QAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;CACpE,QAAQ,WAAW;AACvB,CAAC"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|