@@ -54,7 +54,8 @@ export class LocalFilePermissions {
5454
5555 constructor (
5656 private readonly filesystem : LocalFilesystemService ,
57- private readonly fullFileAccess : ( ) => boolean = ( ) => false
57+ private readonly fullFileAccess : ( ) => boolean = ( ) => false ,
58+ private readonly enableFullFileAccess ?: ( ) => void
5859 ) { }
5960
6061 async authorize (
@@ -71,18 +72,7 @@ export class LocalFilePermissions {
7172 throw new Error ( 'A valid destination workspace and folder are required for imports.' )
7273 const path = await realpath ( nativePath ( authorization . args . path ) )
7374 if ( this . fullFileAccess ( ) ) {
74- const info = await stat ( path )
75- const folder = info . isDirectory ( ) ? path : dirname ( path )
76- const identity = await stat ( folder , { bigint : true } )
77- return this . authorizedAccess (
78- {
79- path,
80- resolve : realpath ,
81- open : ( requested , directory = false ) =>
82- openNativeFile ( folder , relative ( folder , requested ) , identity , directory ) ,
83- } ,
84- { ...context , isCurrent : ( ) => context . isCurrent ( ) && this . fullFileAccess ( ) }
85- )
75+ return this . unrestrictedAccess ( path , context )
8676 }
8777 const existing = await this . filesystem . nativeAccess ( path )
8878 if ( existing ) return this . authorizedAccess ( existing , context )
@@ -114,11 +104,30 @@ export class LocalFilePermissions {
114104 }
115105 pending . contexts . add ( context )
116106 await this . waitForDecision ( pending , context )
107+ if ( this . fullFileAccess ( ) ) return this . unrestrictedAccess ( path , context )
117108 const access = await this . filesystem . nativeAccess ( path )
118109 if ( ! access ) throw new Error ( 'The approved folder is no longer available.' )
119110 return this . authorizedAccess ( access , context )
120111 }
121112
113+ private async unrestrictedAccess (
114+ path : string ,
115+ context : LocalFilePermissionContext
116+ ) : Promise < LocalFileAccess > {
117+ const info = await stat ( path )
118+ const folder = info . isDirectory ( ) ? path : dirname ( path )
119+ const identity = await stat ( folder , { bigint : true } )
120+ return this . authorizedAccess (
121+ {
122+ path,
123+ resolve : realpath ,
124+ open : ( requested , directory = false ) =>
125+ openNativeFile ( folder , relative ( folder , requested ) , identity , directory ) ,
126+ } ,
127+ { ...context , isCurrent : ( ) => context . isCurrent ( ) && this . fullFileAccess ( ) }
128+ )
129+ }
130+
122131 private waitForDecision (
123132 pending : PendingFolderDecision ,
124133 context : LocalFilePermissionContext
@@ -170,7 +179,7 @@ export class LocalFilePermissions {
170179 signal : AbortSignal
171180 ) : Promise < void > {
172181 const context = await this . currentContext ( contexts , signal )
173- if ( await this . filesystem . nativeAccess ( folder ) ) return
182+ if ( this . fullFileAccess ( ) || ( await this . filesystem . nativeAccess ( folder ) ) ) return
174183 const root = await lstat ( folder , { bigint : true } )
175184 if ( ! root . isDirectory ( ) ) throw new Error ( 'The folder is no longer available.' )
176185 const displayedPath = JSON . stringify ( folder ) . replace (
@@ -184,16 +193,42 @@ export class LocalFilePermissions {
184193 signal,
185194 title : 'Allow access to this folder?' ,
186195 message : displayedPath ,
187- detail : `Sim can read files in this folder and its subfolders, use them across chats, and import them into your workspaces on ${ context . origin } .\n\nManage or remove access in File → Folder Access.` ,
188- buttons : [ 'Allow folder' , "Don't allow" ] ,
196+ detail : `Sim can read and import files from this folder and its subfolders across chats on ${ context . origin } .\n\nManage access in File → Folder Access.` ,
197+ buttons : [
198+ 'Allow folder' ,
199+ "Don't allow" ,
200+ ...( this . enableFullFileAccess ? [ 'Full file access' ] : [ ] ) ,
201+ ] ,
189202 defaultId : 1 ,
190203 cancelId : 1 ,
191204 }
192- const result = await ( parent ? showShellDialog ( parent , options ) : showShellDialog ( options ) )
193- signal . throwIfAborted ( )
194- if ( result . response !== 0 ) throw new Error ( 'The user did not allow this local file access.' )
195- const current = await this . currentContext ( contexts , signal )
196- await this . filesystem . grantDirectory ( { path : folder } , current . generation , root )
205+ while ( true ) {
206+ await this . currentContext ( contexts , signal )
207+ const result = await ( parent ? showShellDialog ( parent , options ) : showShellDialog ( options ) )
208+ signal . throwIfAborted ( )
209+ if ( result . response === 2 && this . enableFullFileAccess ) {
210+ const confirmation = {
211+ signal,
212+ title : 'Enable full file access?' ,
213+ message : `Sim can read and import files from any folder on this computer across chats on ${ context . origin } .` ,
214+ detail : 'Turn this off in Settings → Desktop → Full file access.' ,
215+ buttons : [ 'Enable' , 'Cancel' ] ,
216+ defaultId : 1 ,
217+ cancelId : 1 ,
218+ }
219+ const answer = await ( parent
220+ ? showShellDialog ( parent , confirmation )
221+ : showShellDialog ( confirmation ) )
222+ await this . currentContext ( contexts , signal )
223+ if ( answer . response !== 0 ) continue
224+ this . enableFullFileAccess ( )
225+ return
226+ }
227+ if ( result . response !== 0 ) throw new Error ( 'The user did not allow this local file access.' )
228+ const current = await this . currentContext ( contexts , signal )
229+ await this . filesystem . grantDirectory ( { path : folder } , current . generation , root )
230+ return
231+ }
197232 }
198233
199234 private async authorizedAccess (
0 commit comments