summaryrefslogtreecommitdiff
path: root/src/cli/fileTree.ts
diff options
context:
space:
mode:
authorYurenHao0426 <blackhao0426@gmail.com>2026-09-13 04:05:38 +0000
committerYurenHao0426 <blackhao0426@gmail.com>2026-09-13 04:05:38 +0000
commitf3b2fc01082e754f7dfe73caa4f0a4f207a2adfb (patch)
treef6083a5abddc3541ad7996f71695e559b4b41ece /src/cli/fileTree.ts
parent34f22876ab9e15e9f14300a8c21cd7109800d1ab (diff)
Add headless CLI for Overleaf project management (lattex-cli)
New src/cli/ module providing a headless CLI for AI agents to work with Overleaf projects from a server with no display. Commands: auth, projects, clone, pull, status, push, compile. Key design decisions: - Uses Node.js https module (no Electron dependency), same approach as the existing MCP server in src/mcp/lattex.mjs - Reuses overleafProtocol.ts for Socket.IO v0.9 parsing (pure, no Electron) - Separate esbuild bundle (out/cli/lattex-cli.mjs) — does not touch the Electron app build - 64 tests for arg parsing, file tree walking, diff logic, and log parsing Files: - src/cli/main.ts — CLI entry point with all commands - src/cli/args.ts — argument parser - src/cli/overleafApi.ts — Overleaf API client using https + ws - src/cli/fileTree.ts — project root folder walker - src/cli/localState.ts — per-clone state (.lattex-cli.json) and auth storage - src/cli/diff.ts — local vs remote diff logic - src/cli/logParser.ts — LaTeX compile log parser - src/cli/test.ts — unit tests - tsconfig.cli.json — TypeScript config for CLI Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/cli/fileTree.ts')
-rw-r--r--src/cli/fileTree.ts69
1 files changed, 69 insertions, 0 deletions
diff --git a/src/cli/fileTree.ts b/src/cli/fileTree.ts
new file mode 100644
index 0000000..736b1e1
--- /dev/null
+++ b/src/cli/fileTree.ts
@@ -0,0 +1,69 @@
+// Walk Overleaf project root folder into flat maps (same logic as main/index.ts walkRootFolder)
+
+export interface FileTreeEntry {
+ name: string
+ path: string // relative path, forward slashes
+ isDir: boolean
+ docId?: string // text docs
+ fileRefId?: string // binary files
+}
+
+export interface FileTreeResult {
+ entries: FileTreeEntry[]
+ docPathMap: Record<string, string> // docId → relPath
+ pathDocMap: Record<string, string> // relPath → docId
+ fileRefs: Array<{ id: string; path: string }>
+ folderMap: Record<string, string> // folderId → relPath ('' for root)
+ pathFolderMap: Record<string, string> // relPath → folderId
+ rootFolderId: string
+}
+
+interface FolderLike {
+ _id: string
+ name: string
+ docs?: Array<{ _id: string; name: string }>
+ fileRefs?: Array<{ _id: string; name: string }>
+ folders?: FolderLike[]
+}
+
+export function walkRootFolder(rootFolder: FolderLike[]): FileTreeResult {
+ const docPathMap: Record<string, string> = {}
+ const pathDocMap: Record<string, string> = {}
+ const fileRefs: Array<{ id: string; path: string }> = []
+ const folderMap: Record<string, string> = {}
+ const pathFolderMap: Record<string, string> = {}
+ const entries: FileTreeEntry[] = []
+ const rootFolderId = rootFolder[0]?._id || ''
+
+ function walk(f: FolderLike, prefix: string): void {
+ // Register this folder
+ const folderPath = prefix ? prefix.slice(0, -1) : '' // remove trailing /
+ folderMap[f._id] = folderPath
+ pathFolderMap[folderPath] = f._id
+
+ for (const doc of f.docs || []) {
+ const relPath = prefix + doc.name
+ docPathMap[doc._id] = relPath
+ pathDocMap[relPath] = doc._id
+ entries.push({ name: doc.name, path: relPath, isDir: false, docId: doc._id })
+ }
+
+ for (const ref of f.fileRefs || []) {
+ const relPath = prefix + ref.name
+ fileRefs.push({ id: ref._id, path: relPath })
+ entries.push({ name: ref.name, path: relPath, isDir: false, fileRefId: ref._id })
+ }
+
+ for (const sub of f.folders || []) {
+ const subPrefix = prefix + sub.name + '/'
+ entries.push({ name: sub.name, path: subPrefix.slice(0, -1), isDir: true })
+ walk(sub, subPrefix)
+ }
+ }
+
+ if (rootFolder[0]) {
+ walk(rootFolder[0], '')
+ }
+
+ return { entries, docPathMap, pathDocMap, fileRefs, folderMap, pathFolderMap, rootFolderId }
+}