From a55e313de44444d22ef928b1bb91fd96284ea367 Mon Sep 17 00:00:00 2001 From: YurenHao0426 Date: Sun, 13 Sep 2026 04:24:07 +0000 Subject: Add copy command to clone Overleaf projects server-side Adds `lattex-cli copy ` which uses Overleaf's POST /project/{id}/clone endpoint to create a server-side copy of a project. Includes API method, CLI wiring, help text, and tests. Co-Authored-By: Claude Opus 5 --- src/cli/main.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'src/cli/main.ts') diff --git a/src/cli/main.ts b/src/cli/main.ts index 1fa9fce..52573fe 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -186,6 +186,37 @@ async function cmdProjects(flags: Record): Promise): Promise { + const sourceRef = positional[0] + const newName = positional[1] + if (!sourceRef || !newName) { + exitWith(EXIT_USAGE, 'Usage: lattex-cli copy ') + } + + const cookie = await resolveAuth(flags) + const api = new OverleafApi(cookie) + + // Resolve project ID from name if needed + let sourceId = sourceRef + if (!sourceRef.match(/^[0-9a-f]{24}$/)) { + const projects = await api.listProjects() + const match = projects.find(p => p.name === sourceRef) + if (!match) { + exitWith(EXIT_ERROR, `Project not found: ${sourceRef}`) + } + sourceId = match!.id + } + + err(`Copying project ${sourceId} as "${newName}"...`) + const newId = await api.copyProject(sourceId, newName) + + if (jsonMode) { + jsonOut({ ok: true, sourceId, newId, newName }) + } else { + out(`Copied → ${newId} "${newName}"`) + } +} + async function cmdClone(positional: string[], flags: Record): Promise { const projectRef = positional[0] const targetDir = positional[1] @@ -693,6 +724,7 @@ Commands: projects [--json] List Overleaf projects + copy Copy (clone) a project on Overleaf clone Download project to local directory pull Update local dir from Overleaf status Show local changes @@ -726,6 +758,9 @@ async function main(): Promise { case 'projects': await cmdProjects(parsed.flags) break + case 'copy': + await cmdCopy(parsed.positional, parsed.flags) + break case 'clone': await cmdClone(parsed.positional, parsed.flags) break -- cgit v1.2.3