summaryrefslogtreecommitdiff
path: root/src/cli/main.ts
diff options
context:
space:
mode:
authorYurenHao0426 <blackhao0426@gmail.com>2026-09-13 04:24:07 +0000
committerYurenHao0426 <blackhao0426@gmail.com>2026-09-13 04:24:07 +0000
commita55e313de44444d22ef928b1bb91fd96284ea367 (patch)
tree7273cbee484effff4878fa81a8a3313e14695aba /src/cli/main.ts
parent55bf3f24c57521907a1bac7180aa29ea8cbf83d7 (diff)
Add copy command to clone Overleaf projects server-sidefix-cli-shebang
Adds `lattex-cli copy <source-id|name> <new-name>` 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 <noreply@anthropic.com>
Diffstat (limited to 'src/cli/main.ts')
-rw-r--r--src/cli/main.ts35
1 files changed, 35 insertions, 0 deletions
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<string, string | boolean>): Promise<voi
}
}
+async function cmdCopy(positional: string[], flags: Record<string, string | boolean>): Promise<void> {
+ const sourceRef = positional[0]
+ const newName = positional[1]
+ if (!sourceRef || !newName) {
+ exitWith(EXIT_USAGE, 'Usage: lattex-cli copy <source-id|name> <new-name>')
+ }
+
+ 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<string, string | boolean>): Promise<void> {
const projectRef = positional[0]
const targetDir = positional[1]
@@ -693,6 +724,7 @@ Commands:
projects [--json] List Overleaf projects
+ copy <id|name> <name> Copy (clone) a project on Overleaf
clone <id|name> <dir> Download project to local directory
pull <dir> Update local dir from Overleaf
status <dir> Show local changes
@@ -726,6 +758,9 @@ async function main(): Promise<void> {
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