@@ -13,56 +13,55 @@ const Terminal = {
1313 system . getFilesDir ( resolve , reject ) ;
1414 } ) ;
1515
16- if ( installing ) {
17- return new Promise ( ( resolve , reject ) => {
18- readAsset ( "init-alpine.sh" , async ( content ) => {
19- system . writeText ( `${ filesDir } /init-alpine.sh` , content , logger , err_logger ) ;
20- } ) ;
21-
22- readAsset ( "rm-wrapper.sh" , async ( content ) => {
23- system . deleteFile ( `${ filesDir } /alpine/bin/rm` , logger , err_logger ) ;
24- system . writeText ( `${ filesDir } /alpine/bin/rm` , content , logger , err_logger ) ;
25- system . setExec ( `${ filesDir } /alpine/bin/rm` , true , logger , err_logger ) ;
26- } ) ;
27-
28- readAsset ( "init-sandbox.sh" , ( content ) => {
29- system . writeText ( `${ filesDir } /init-sandbox.sh` , content , logger , err_logger ) ;
30-
31- Executor . start ( "sh" , ( type , data ) => {
32- logger ( `${ type } ${ data } ` ) ;
16+ const [ initAlpine , rmWrapper , initSandbox ] = await Promise . all ( [
17+ readAsset ( "init-alpine.sh" ) ,
18+ readAsset ( "rm-wrapper.sh" ) ,
19+ readAsset ( "init-sandbox.sh" ) ,
20+ ] ) ;
3321
34- // Check for exit code during installation
35- if ( type === "exit" ) {
36- resolve ( data === "0" ) ;
37- }
38- } ) . then ( async ( uuid ) => {
39- await Executor . write ( uuid , `source ${ filesDir } /init-sandbox.sh ${ installing ? "--installing" : "" } ; exit` ) ;
40- } ) . catch ( ( error ) => {
41- err_logger ( "Failed to start AXS:" , error ) ;
42- resolve ( false ) ;
43- } ) ;
44- } ) ;
45- } ) ;
46- } else {
47- readAsset ( "rm-wrapper.sh" , async ( content ) => {
48- system . deleteFile ( `${ filesDir } /alpine/bin/rm` , logger , err_logger ) ;
49- system . writeText ( `${ filesDir } /alpine/bin/rm` , content , logger , err_logger ) ;
50- system . setExec ( `${ filesDir } /alpine/bin/rm` , true , logger , err_logger ) ;
51- } ) ;
22+ await writeText ( `${ filesDir } /init-alpine.sh` , initAlpine ) ;
23+ await writeText ( `${ filesDir } /init-sandbox.sh` , initSandbox ) ;
5224
53- readAsset ( "init- alpine.sh" , async ( content ) => {
54- system . writeText ( `${ filesDir } /init- alpine.sh ` , content , logger , err_logger ) ;
55- } ) ;
25+ await deleteFile ( ` ${ filesDir } / alpine/bin/rm` ) . catch ( ( ) => { } ) ;
26+ await writeText ( `${ filesDir } /alpine/bin/rm ` , rmWrapper ) ;
27+ await setExec ( ` ${ filesDir } /alpine/bin/rm` , true ) ;
5628
57- readAsset ( "init-sandbox.sh" , ( content ) => {
58- system . writeText ( `${ filesDir } /init-sandbox.sh` , content , logger , err_logger ) ;
29+ if ( installing ) {
30+ return new Promise ( ( resolve , reject ) => {
31+ let lastError = "" ;
5932
6033 Executor . start ( "sh" , ( type , data ) => {
6134 logger ( `${ type } ${ data } ` ) ;
35+
36+ if ( type === "stderr" && data ) {
37+ lastError = lastError ? `${ lastError } \n${ data } ` : data ;
38+ }
39+
40+ // Check for exit code during installation
41+ if ( type === "exit" ) {
42+ const success = data === "0" ;
43+ if ( ! success ) {
44+ this . lastInstallError = lastError
45+ ? `Sandbox configuration failed with exit code ${ data } : ${ lastError } `
46+ : `Sandbox configuration failed with exit code ${ data } ` ;
47+ }
48+ resolve ( success ) ;
49+ }
6250 } ) . then ( async ( uuid ) => {
6351 await Executor . write ( uuid , `source ${ filesDir } /init-sandbox.sh ${ installing ? "--installing" : "" } ; exit` ) ;
52+ } ) . catch ( ( error ) => {
53+ const message = `Failed to start AXS: ${ formatError ( error ) } ` ;
54+ this . lastInstallError = message ;
55+ err_logger ( message ) ;
56+ resolve ( false ) ;
6457 } ) ;
6558 } ) ;
59+ } else {
60+ Executor . start ( "sh" , ( type , data ) => {
61+ logger ( `${ type } ${ data } ` ) ;
62+ } ) . then ( async ( uuid ) => {
63+ await Executor . write ( uuid , `source ${ filesDir } /init-sandbox.sh ${ installing ? "--installing" : "" } ; exit` ) ;
64+ } ) ;
6665 }
6766 } ,
6867
@@ -104,6 +103,7 @@ const Terminal = {
104103 */
105104 async install ( logger = console . log , err_logger = console . error ) {
106105 if ( ! ( await this . isSupported ( ) ) ) return false ;
106+ this . lastInstallError = "" ;
107107
108108 try {
109109 //cleanup before insatll
@@ -154,62 +154,26 @@ const Terminal = {
154154
155155
156156 logger ( "⬇️ Downloading sandbox filesystem..." ) ;
157- await new Promise ( ( resolve , reject ) => {
158- cordova . plugin . http . downloadFile (
159- alpineUrl , { } , { } ,
160- cordova . file . dataDirectory + "alpine.tar.gz" ,
161- resolve , reject
162- ) ;
163- } ) ;
157+ await downloadFile ( alpineUrl , cordova . file . dataDirectory + "alpine.tar.gz" , "Sandbox filesystem" ) ;
164158
165159 logger ( "⬇️ Downloading axs..." ) ;
166- await new Promise ( ( resolve , reject ) => {
167- cordova . plugin . http . downloadFile (
168- axsUrl , { } , { } ,
169- cordova . file . dataDirectory + "axs" ,
170- resolve , reject
171- ) ;
172- } ) ;
160+ await downloadFile ( axsUrl , cordova . file . dataDirectory + "axs" , "AXS" ) ;
173161
174162 const isFdroid = await Executor . execute ( "echo $FDROID" ) ;
175163 if ( isFdroid === "true" ) {
176164 logger ( "🐧 F-Droid flavor detected, downloading additional files..." ) ;
177165 logger ( "⬇️ Downloading compatibility layer..." ) ;
178- await new Promise ( ( resolve , reject ) => {
179- cordova . plugin . http . downloadFile (
180- prootUrl , { } , { } ,
181- cordova . file . dataDirectory + "libproot-xed.so" ,
182- resolve , reject
183- ) ;
184- } ) ;
166+ await downloadFile ( prootUrl , cordova . file . dataDirectory + "libproot-xed.so" , "Compatibility layer" ) ;
185167
186168 logger ( "⬇️ Downloading supporting library..." ) ;
187- await new Promise ( ( resolve , reject ) => {
188- cordova . plugin . http . downloadFile (
189- libTalloc , { } , { } ,
190- cordova . file . dataDirectory + "libtalloc.so.2" ,
191- resolve , reject
192- ) ;
193- } ) ;
169+ await downloadFile ( libTalloc , cordova . file . dataDirectory + "libtalloc.so.2" , "Supporting library" ) ;
194170
195171 if ( libproot != null ) {
196- await new Promise ( ( resolve , reject ) => {
197- cordova . plugin . http . downloadFile (
198- libproot , { } , { } ,
199- cordova . file . dataDirectory + "libproot.so" ,
200- resolve , reject
201- ) ;
202- } ) ;
172+ await downloadFile ( libproot , cordova . file . dataDirectory + "libproot.so" , "proot loader" ) ;
203173 }
204174
205175 if ( libproot32 != null ) {
206- await new Promise ( ( resolve , reject ) => {
207- cordova . plugin . http . downloadFile (
208- libproot32 , { } , { } ,
209- cordova . file . dataDirectory + "libproot32.so" ,
210- resolve , reject
211- ) ;
212- } ) ;
176+ await downloadFile ( libproot32 , cordova . file . dataDirectory + "libproot32.so" , "32-bit proot loader" ) ;
213177 }
214178
215179 }
@@ -218,39 +182,37 @@ const Terminal = {
218182
219183 logger ( "📁 Setting up directories..." ) ;
220184
221- await new Promise ( ( resolve , reject ) => {
222- system . mkdirs ( `${ filesDir } /.downloaded` , resolve , reject ) ;
223- } ) ;
185+ await ensureDir ( `${ filesDir } /.downloaded` ) ;
224186
225187 const alpineDir = `${ filesDir } /alpine` ;
226188
227- await new Promise ( ( resolve , reject ) => {
228- system . mkdirs ( alpineDir , resolve , reject ) ;
229- } ) ;
189+ await ensureDir ( alpineDir ) ;
230190
231191 logger ( "📦 Extracting sandbox filesystem..." ) ;
232192 await Executor . execute ( `tar --no-same-owner -xf ${ filesDir } /alpine.tar.gz -C ${ alpineDir } ` ) ;
233193
234194 logger ( "⚙️ Applying basic configuration..." ) ;
235- system . writeText ( `${ alpineDir } /etc/resolv.conf` , `nameserver 8.8.4.4 \nnameserver 8.8.8.8` ) ;
195+ await writeText ( `${ alpineDir } /etc/resolv.conf` , `nameserver 8.8.4.4 \nnameserver 8.8.8.8` ) ;
236196
237- readAsset ( "rm-wrapper.sh" , async ( content ) => {
238- system . deleteFile ( `${ alpineDir } /bin/rm` , logger , err_logger ) ;
239- system . writeText ( `${ alpineDir } /bin/rm` , content , logger , err_logger ) ;
240- system . setExec ( `${ alpineDir } /bin/rm` , true , logger , err_logger ) ;
241- } ) ;
197+ const rmWrapper = await readAsset ( "rm-wrapper.sh" ) ;
198+ await deleteFile ( `${ alpineDir } /bin/rm` ) . catch ( ( ) => { } ) ;
199+ await writeText ( `${ alpineDir } /bin/rm` , rmWrapper ) ;
200+ await setExec ( `${ alpineDir } /bin/rm` , true ) ;
242201
243202 logger ( "✅ Extraction complete" ) ;
244- await new Promise ( ( resolve , reject ) => {
245- system . mkdirs ( `${ filesDir } /.extracted` , resolve , reject ) ;
246- } ) ;
203+ await ensureDir ( `${ filesDir } /.extracted` ) ;
247204
248205 logger ( "⚙️ Updating sandbox enviroment..." ) ;
249206 const installResult = await this . startAxs ( true , logger , err_logger ) ;
207+ if ( ! installResult ) {
208+ throw new Error ( this . lastInstallError || "Sandbox configuration failed." ) ;
209+ }
250210 return installResult ;
251211
252212 } catch ( e ) {
253- err_logger ( "Installation failed:" , e ) ;
213+ const message = formatError ( e ) ;
214+ this . lastInstallError = message ;
215+ err_logger ( `Installation failed: ${ message } ` ) ;
254216 console . error ( "Installation failed:" , e ) ;
255217 return false ;
256218 }
@@ -439,20 +401,99 @@ const Terminal = {
439401 reject ( result ) ;
440402 }
441403 } ) ;
442- }
404+ } ,
405+
406+ formatError
443407} ;
444408
445409
446410function readAsset ( assetPath , callback ) {
447411 const assetUrl = "file:///android_asset/" + assetPath ;
448412
449- window . resolveLocalFileSystemURL ( assetUrl , fileEntry => {
450- fileEntry . file ( file => {
451- const reader = new FileReader ( ) ;
452- reader . onloadend = ( ) => callback ( reader . result ) ;
453- reader . readAsText ( file ) ;
454- } , console . error ) ;
455- } , console . error ) ;
413+ const promise = new Promise ( ( resolve , reject ) => {
414+ window . resolveLocalFileSystemURL ( assetUrl , fileEntry => {
415+ fileEntry . file ( file => {
416+ const reader = new FileReader ( ) ;
417+ reader . onloadend = ( ) => resolve ( reader . result ) ;
418+ reader . onerror = ( ) => reject ( reader . error || new Error ( `Failed to read ${ assetPath } ` ) ) ;
419+ reader . readAsText ( file ) ;
420+ } , reject ) ;
421+ } , reject ) ;
422+ } ) ;
423+
424+ if ( callback ) {
425+ promise . then ( callback ) . catch ( console . error ) ;
426+ }
427+
428+ return promise ;
429+ }
430+
431+ function fileExists ( path ) {
432+ return new Promise ( ( resolve , reject ) => {
433+ system . fileExists ( path , false , ( result ) => {
434+ resolve ( result == 1 ) ;
435+ } , reject ) ;
436+ } ) ;
437+ }
438+
439+ async function ensureDir ( path ) {
440+ if ( await fileExists ( path ) ) return ;
441+
442+ await new Promise ( ( resolve , reject ) => {
443+ system . mkdirs ( path , resolve , reject ) ;
444+ } ) ;
445+ }
446+
447+ function writeText ( path , content ) {
448+ return new Promise ( ( resolve , reject ) => {
449+ system . writeText ( path , content , resolve , reject ) ;
450+ } ) ;
451+ }
452+
453+ function deleteFile ( path ) {
454+ return new Promise ( ( resolve , reject ) => {
455+ system . deleteFile ( path , resolve , reject ) ;
456+ } ) ;
457+ }
458+
459+ function setExec ( path , executable ) {
460+ return new Promise ( ( resolve , reject ) => {
461+ system . setExec ( path , executable , resolve , reject ) ;
462+ } ) ;
463+ }
464+
465+ function downloadFile ( url , destination , label ) {
466+ return new Promise ( ( resolve , reject ) => {
467+ cordova . plugin . http . downloadFile (
468+ url , { } , { } ,
469+ destination ,
470+ resolve ,
471+ ( error ) => reject ( new Error ( `${ label } download failed: ${ formatError ( error ) } ` ) )
472+ ) ;
473+ } ) ;
474+ }
475+
476+ function formatError ( error ) {
477+ if ( error == null ) return "Unknown error" ;
478+ if ( error instanceof Error ) return error . message || String ( error ) ;
479+ if ( typeof error === "string" ) return error || "Unknown error" ;
480+ if ( typeof error === "object" ) {
481+ const parts = [ ] ;
482+ if ( error . status != null ) parts . push ( `status ${ error . status } ` ) ;
483+ if ( error . error ) parts . push ( String ( error . error ) ) ;
484+ if ( error . message ) parts . push ( String ( error . message ) ) ;
485+ if ( error . exception ) parts . push ( String ( error . exception ) ) ;
486+ if ( error . url ) parts . push ( `URL: ${ error . url } ` ) ;
487+ if ( parts . length ) return parts . join ( " - " ) ;
488+
489+ try {
490+ return JSON . stringify ( error ) ;
491+ } catch ( jsonError ) {
492+ return String ( error ) ;
493+ }
494+ }
495+
496+ return String ( error ) ;
456497}
457498
458- module . exports = Terminal ;
499+ module . exports = Terminal ;
0 commit comments