@@ -31,6 +31,129 @@ function getWorkspaceEntryCount(baseDir) {
3131 return countVisibleEntries ( path . join ( baseDir , 'workspaces' ) ) ;
3232}
3333
34+ function isSameFileContent ( sourcePath , targetPath ) {
35+ const fs = require ( 'fs' ) ;
36+ try {
37+ const sourceStat = fs . statSync ( sourcePath ) ;
38+ const targetStat = fs . statSync ( targetPath ) ;
39+ if ( sourceStat . size !== targetStat . size ) return false ;
40+ const sourceContent = fs . readFileSync ( sourcePath ) ;
41+ const targetContent = fs . readFileSync ( targetPath ) ;
42+ return sourceContent . equals ( targetContent ) ;
43+ } catch {
44+ return false ;
45+ }
46+ }
47+
48+ function copyTreeSync ( sourcePath , targetPath ) {
49+ const fs = require ( 'fs' ) ;
50+ const stat = fs . lstatSync ( sourcePath ) ;
51+ if ( stat . isDirectory ( ) ) {
52+ fs . mkdirSync ( targetPath , { recursive : true } ) ;
53+ for ( const entry of fs . readdirSync ( sourcePath ) ) {
54+ copyTreeSync ( path . join ( sourcePath , entry ) , path . join ( targetPath , entry ) ) ;
55+ }
56+ return ;
57+ }
58+
59+ fs . mkdirSync ( path . dirname ( targetPath ) , { recursive : true } ) ;
60+ fs . copyFileSync ( sourcePath , targetPath ) ;
61+ }
62+
63+ function mergeLegacyDataDir ( ) {
64+ const fs = require ( 'fs' ) ;
65+
66+ // Skip if env override is set (user chose a custom path)
67+ if ( String ( process . env . AGENT_WORKSPACE_DIR || '' ) . trim ( ) ) {
68+ return { merged : false , reason : 'env-override' } ;
69+ }
70+
71+ const state = getLegacyCompatibilityState ( ) ;
72+ if ( ! state . shouldUseLegacyDir ) {
73+ return {
74+ merged : false ,
75+ reason : state . reason ,
76+ sourceDir : state . oldDir ,
77+ targetDir : state . newDir
78+ } ;
79+ }
80+
81+ const sourceDir = state . oldDir ;
82+ const targetDir = state . newDir ;
83+ const backupRoot = path . join (
84+ targetDir ,
85+ 'migration-backups' ,
86+ `from-orchestrator-${ new Date ( ) . toISOString ( ) . replace ( / [: .] / g, '-' ) } `
87+ ) ;
88+ const report = {
89+ merged : false ,
90+ reason : state . reason ,
91+ sourceDir,
92+ targetDir,
93+ backupDir : backupRoot ,
94+ copied : [ ] ,
95+ overwritten : [ ]
96+ } ;
97+
98+ if ( ! fs . existsSync ( sourceDir ) ) {
99+ return { ...report , merged : false , reason : 'legacy-missing' } ;
100+ }
101+
102+ fs . mkdirSync ( targetDir , { recursive : true } ) ;
103+
104+ const mergeEntry = ( srcPath , dstPath , relPath = '' ) => {
105+ const srcStat = fs . lstatSync ( srcPath ) ;
106+ const dstExists = fs . existsSync ( dstPath ) ;
107+
108+ if ( srcStat . isDirectory ( ) ) {
109+ if ( ! dstExists ) {
110+ copyTreeSync ( srcPath , dstPath ) ;
111+ report . copied . push ( relPath || path . basename ( srcPath ) ) ;
112+ return ;
113+ }
114+
115+ const dstStat = fs . lstatSync ( dstPath ) ;
116+ if ( ! dstStat . isDirectory ( ) ) {
117+ const backupPath = path . join ( backupRoot , relPath ) ;
118+ copyTreeSync ( dstPath , backupPath ) ;
119+ report . overwritten . push ( relPath ) ;
120+ fs . rmSync ( dstPath , { recursive : true , force : true } ) ;
121+ copyTreeSync ( srcPath , dstPath ) ;
122+ return ;
123+ }
124+
125+ for ( const entry of fs . readdirSync ( srcPath ) ) {
126+ const nextRel = relPath ? path . join ( relPath , entry ) : entry ;
127+ mergeEntry ( path . join ( srcPath , entry ) , path . join ( dstPath , entry ) , nextRel ) ;
128+ }
129+ return ;
130+ }
131+
132+ if ( ! dstExists ) {
133+ copyTreeSync ( srcPath , dstPath ) ;
134+ report . copied . push ( relPath || path . basename ( srcPath ) ) ;
135+ return ;
136+ }
137+
138+ const dstStat = fs . lstatSync ( dstPath ) ;
139+ if ( dstStat . isDirectory ( ) || ! isSameFileContent ( srcPath , dstPath ) ) {
140+ const backupPath = path . join ( backupRoot , relPath ) ;
141+ copyTreeSync ( dstPath , backupPath ) ;
142+ fs . mkdirSync ( path . dirname ( dstPath ) , { recursive : true } ) ;
143+ fs . copyFileSync ( srcPath , dstPath ) ;
144+ report . overwritten . push ( relPath || path . basename ( srcPath ) ) ;
145+ }
146+ } ;
147+
148+ for ( const entry of fs . readdirSync ( sourceDir ) ) {
149+ if ( entry === 'migration-backups' ) continue ;
150+ mergeEntry ( path . join ( sourceDir , entry ) , path . join ( targetDir , entry ) , entry ) ;
151+ }
152+
153+ report . merged = report . copied . length > 0 || report . overwritten . length > 0 ;
154+ return report ;
155+ }
156+
34157function getLegacyCompatibilityState ( ) {
35158 const fs = require ( 'fs' ) ;
36159 const newDir = getDefaultAgentWorkspaceDir ( ) ;
@@ -275,6 +398,7 @@ module.exports = {
275398 getDefaultAgentWorkspaceDir,
276399 getLegacyAgentWorkspaceDir,
277400 getLegacyCompatibilityState,
401+ mergeLegacyDataDir,
278402 getProjectsRoot,
279403 getLegacyProjectsRoot,
280404 migrateFromOrchestratorDir,
0 commit comments