* Write remote team memory entries to the local directory. * Validates every path against the team memory directory boundary. * Skips entries whose on-disk content already matches, so unchanged * files keep their mtime and don't spuriously invalidate the * getMemoryFiles cache or trigger watcher
( entries: Record<string, string>, )
| 687 | * Returns the number of files actually written. |
| 688 | */ |
| 689 | async function writeRemoteEntriesToLocal( |
| 690 | entries: Record<string, string>, |
| 691 | ): Promise<number> { |
| 692 | const results = await Promise.all( |
| 693 | Object.entries(entries).map(async ([relPath, content]) => { |
| 694 | let validatedPath: string |
| 695 | try { |
| 696 | validatedPath = await validateTeamMemKey(relPath) |
| 697 | } catch (e) { |
| 698 | if (e instanceof PathTraversalError) { |
| 699 | logForDebugging(`team-memory-sync: ${e.message}`, { level: 'warn' }) |
| 700 | return false |
| 701 | } |
| 702 | throw e |
| 703 | } |
| 704 | |
| 705 | const sizeBytes = Buffer.byteLength(content, 'utf8') |
| 706 | if (sizeBytes > MAX_FILE_SIZE_BYTES) { |
| 707 | logForDebugging( |
| 708 | `team-memory-sync: skipping oversized remote entry "${relPath}"`, |
| 709 | { level: 'info' }, |
| 710 | ) |
| 711 | return false |
| 712 | } |
| 713 | |
| 714 | // Skip if on-disk content already matches. Handles the common case |
| 715 | // where pull returns unchanged entries (skipEtagCache path, first |
| 716 | // pull of a session with warm disk state from prior session). |
| 717 | try { |
| 718 | const existing = await readFile(validatedPath, 'utf8') |
| 719 | if (existing === content) { |
| 720 | return false |
| 721 | } |
| 722 | } catch (e) { |
| 723 | if ( |
| 724 | isErrnoException(e) && |
| 725 | e.code !== 'ENOENT' && |
| 726 | e.code !== 'ENOTDIR' |
| 727 | ) { |
| 728 | logForDebugging( |
| 729 | `team-memory-sync: unexpected read error for "${relPath}": ${e.code}`, |
| 730 | { level: 'debug' }, |
| 731 | ) |
| 732 | } |
| 733 | // Fall through to write for ENOENT/ENOTDIR (file doesn't exist yet) |
| 734 | } |
| 735 | |
| 736 | try { |
| 737 | const parentDir = validatedPath.substring( |
| 738 | 0, |
| 739 | validatedPath.lastIndexOf(sep), |
| 740 | ) |
| 741 | await mkdir(parentDir, { recursive: true }) |
| 742 | await writeFile(validatedPath, content, 'utf8') |
| 743 | return true |
| 744 | } catch (e) { |
| 745 | logForDebugging( |
| 746 | `team-memory-sync: failed to write "${relPath}": ${e}`, |
no test coverage detected