diff options
| author | YurenHao0426 <blackhao0426@gmail.com> | 2026-09-13 04:05:38 +0000 |
|---|---|---|
| committer | YurenHao0426 <blackhao0426@gmail.com> | 2026-09-13 04:05:38 +0000 |
| commit | f3b2fc01082e754f7dfe73caa4f0a4f207a2adfb (patch) | |
| tree | f6083a5abddc3541ad7996f71695e559b4b41ece /src/cli/fileTree.ts | |
| parent | 34f22876ab9e15e9f14300a8c21cd7109800d1ab (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.ts | 69 |
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 } +} |
