@@ -11,6 +11,7 @@ const FlashCommand = require('./flash');
11
11
const path = require ( 'path' ) ;
12
12
const _ = require ( 'lodash' ) ;
13
13
const chalk = require ( 'chalk' ) ;
14
+ const { clear } = require ( 'console' ) ;
14
15
15
16
// TODO: Get these from exports
16
17
const PROVISIONING_PROGRESS = 1 ;
@@ -78,14 +79,38 @@ module.exports = class ESimCommands extends CLICommandBase {
78
79
console . log ( 'Ready to bulk provision. Connect devices to start. Press Ctrl-C to exit.' ) ;
79
80
}
80
81
81
- async enableCommand ( ) {
82
+ async enableCommand ( iccid ) {
83
+ console . log ( chalk . bold ( `Ensure only one device is connected${ os . EOL } ` ) ) ;
82
84
this . verbose = true ;
83
85
const device = await this . serial . whatSerialPortDidYouMean ( ) ;
84
- if ( device . type === 'Tachyon' ) {
85
- this . isTachyon = true ;
86
+ if ( device . type !== 'Tachyon' ) {
87
+ throw new Error ( 'Enable command is only for Tachyon devices' ) ;
88
+ }
89
+ this . isTachyon = true ;
90
+ await this . doEnable ( iccid ) ;
91
+ }
92
+
93
+ async deleteCommand ( args , iccid ) {
94
+ console . log ( chalk . bold ( `Ensure only one device is connected${ os . EOL } ` ) ) ;
95
+ this . verbose = true ;
96
+ const device = await this . serial . whatSerialPortDidYouMean ( ) ;
97
+ if ( device . type !== 'Tachyon' ) {
98
+ throw new Error ( 'Delete command is only for Tachyon devices' ) ;
86
99
}
100
+ this . isTachyon = true ;
101
+ this . _validateArgs ( args ) ;
102
+ await this . doDelete ( device , iccid ) ;
103
+ }
87
104
88
- await this . doEnable ( device ) ;
105
+ async listCommand ( ) {
106
+ console . log ( chalk . bold ( `Ensure only one device is connected${ os . EOL } ` ) ) ;
107
+ this . verbose = true ;
108
+ const device = await this . serial . whatSerialPortDidYouMean ( ) ;
109
+ if ( device . type !== 'Tachyon' ) {
110
+ throw new Error ( 'List command is only for Tachyon devices' ) ;
111
+ }
112
+ this . isTachyon = true ;
113
+ await this . doList ( ) ;
89
114
}
90
115
91
116
// Populate the availableProvisioningData set with the indices of the input JSON data
@@ -256,98 +281,141 @@ module.exports = class ESimCommands extends CLICommandBase {
256
281
}
257
282
}
258
283
259
- async doEnable ( device ) {
260
- let provisionOutputLogs = [ ] ;
261
- let timestamp = new Date ( ) . toISOString ( ) . replace ( / : / g, '-' ) ;
262
- let success = false ;
263
-
264
- const outputJsonFile = path . join ( this . outputFolder , `${ this . isTachyon ? 'tachyon' : device . deviceId } _${ timestamp } .json` ) ;
265
-
266
- const processOutput = async ( failedLogs = [ ] ) => {
267
- const logs = Array . isArray ( failedLogs ) ? failedLogs : [ failedLogs ] ;
268
- provisionOutputLogs . push ( {
269
- step : 'final_step' ,
270
- timestamp : new Date ( ) . toISOString ( ) . replace ( / : / g, '-' ) ,
271
- success : success ? 'success' : 'failed' ,
272
- details : {
273
- rawLogs : success ? [ 'Profile enable successful' ] : [ 'Profile enable failed' , ...logs ] ,
274
- }
284
+ async doEnable ( iccid ) {
285
+ const TACHYON_QLRIL_WAIT_TIMEOUT = 20000 ;
286
+ let output = '' ;
287
+
288
+ try {
289
+ this . adbProcess = execa ( 'adb' , [ 'shell' , 'qlril-app' , 'enable' , iccid ] ) ;
290
+
291
+ await new Promise ( ( resolve , reject ) => {
292
+ const timeout = setTimeout ( ( ) => {
293
+ reject ( new Error ( 'Timeout waiting for qlril app to start' ) ) ;
294
+ } , TACHYON_QLRIL_WAIT_TIMEOUT ) ;
295
+
296
+ this . adbProcess . stdout . on ( 'data' , ( data ) => {
297
+ output += data . toString ( ) ;
298
+ if ( output . includes ( `ICCID currently active: ${ iccid } ` ) ) {
299
+ console . log ( `ICCID ${ iccid } enabled successfully` ) ;
300
+ clearTimeout ( timeout ) ;
301
+ resolve ( ) ;
302
+ }
303
+ } ) ;
304
+
305
+ this . adbProcess . catch ( ( error ) => {
306
+ clearTimeout ( timeout ) ;
307
+ reject ( new Error ( `ADB process error: ${ error . message } ` ) ) ;
308
+ } ) ;
309
+
310
+ this . adbProcess . then ( ( ) => {
311
+ clearTimeout ( timeout ) ;
312
+ reject ( new Error ( 'ADB process ended early without valid output' ) ) ;
313
+ } ) ;
275
314
} ) ;
276
- await this . _changeLed ( device , success ? PROVISIONING_SUCCESS : PROVISIONING_FAILURE ) ;
277
- this . _addToJson ( outputJsonFile , provisionOutputLogs . filter ( Boolean ) ) ;
278
- } ;
315
+
316
+ console . log ( os . EOL ) ;
317
+ } catch ( error ) {
318
+ console . error ( `Failed to enable profiles: ${ error . message } ` ) ;
319
+ } finally {
320
+ this . _exitQlril ( ) ;
321
+ }
322
+ }
279
323
324
+ async doDelete ( device , iccid ) {
280
325
try {
281
326
const port = device . port ;
282
-
283
- // Start qlril-app through ADB for Tachyon
284
- const qlrilStep = await this . _initializeQlril ( ) ;
285
- provisionOutputLogs . push ( qlrilStep ) ;
286
- if ( qlrilStep ?. status === 'failed' ) {
287
- await processOutput ( ) ;
288
- return ;
289
- }
290
327
291
- const iccidsOnDevice = await this . _getIccidOnDevice ( port ) ;
292
-
293
- const iccidToEnable = this . _getIccidToEnable ( iccidsOnDevice ) ;
294
- provisionOutputLogs . push ( `ICCID to enable: ${ iccidToEnable } ` ) ;
295
- if ( iccidToEnable === null ) {
296
- await processOutput ( 'No profile found on the device to enable' ) ;
297
- return ;
298
- }
299
-
300
- const enableResp = await this . _enableProfile ( port , iccidToEnable ) ;
301
- provisionOutputLogs . push ( enableResp ) ;
302
- if ( enableResp . status === 'failed' ) {
303
- await processOutput ( ) ;
304
- return ;
305
- }
328
+ await this . _initializeQlril ( ) ;
306
329
307
- const verifyIccidEnabledResp = await this . _verifyIccidEnaled ( port , iccidToEnable ) ;
308
- provisionOutputLogs . push ( verifyIccidEnabledResp ) ;
309
- if ( verifyIccidEnabledResp . status === 'failed' ) {
310
- await processOutput ( ) ;
330
+ const iccidsOnDevice = await this . _getIccidOnDevice ( port ) ;
331
+ console . log ( 'Profiles on device:' , iccidsOnDevice ) ;
332
+ if ( ! iccidsOnDevice . includes ( iccid ) ) {
333
+ console . log ( `ICCID ${ iccid } not found on the device or is a test ICCID` ) ;
311
334
return ;
312
335
}
313
-
314
- success = true ;
315
- console . log ( 'Profile enabled successfully' ) ;
316
- await processOutput ( ) ;
336
+
337
+ await execa ( this . lpa , [ 'disable' , iccid , `--serial=${ port } ` ] ) ;
338
+ await execa ( this . lpa , [ 'delete' , iccid , `--serial=${ port } ` ] ) ;
339
+
340
+ console . log ( 'Profile deleted successfully' ) ;
317
341
} catch ( error ) {
318
- await processOutput ( error . message ) ;
342
+ console . error ( `Failed to delete profile: ${ error . message } ` ) ;
319
343
} finally {
320
344
this . _exitQlril ( ) ;
321
345
}
322
346
}
323
347
324
- _validateArgs ( args ) {
325
- if ( ! args ) {
326
- throw new Error ( 'Missing args' ) ;
348
+ async doList ( ) {
349
+ const TACHYON_QLRIL_WAIT_TIMEOUT = 10000 ;
350
+ let output = '' ;
351
+
352
+ try {
353
+ this . adbProcess = execa ( 'adb' , [ 'shell' , 'qlril-app' , 'listProfiles' ] ) ;
354
+
355
+ await new Promise ( ( resolve , reject ) => {
356
+ const timeout = setTimeout ( ( ) => {
357
+ reject ( new Error ( 'Timeout waiting for qlril app to start' ) ) ;
358
+ } , TACHYON_QLRIL_WAIT_TIMEOUT ) ;
359
+
360
+ this . adbProcess . stdout . on ( 'data' , ( data ) => {
361
+ output += data . toString ( ) ;
362
+
363
+ const iccids = output
364
+ . trim ( )
365
+ . replace ( / ^ \[ / , '' )
366
+ . replace ( / \] $ / , '' )
367
+ . split ( ',' )
368
+ . map ( iccid => iccid . trim ( ) )
369
+ . filter ( Boolean ) ;
370
+
371
+ if ( iccids . length > 0 ) {
372
+ console . log ( `Profiles found:${ os . EOL } ` ) ;
373
+ iccids . forEach ( iccid => console . log ( `\t- ${ iccid } ` ) ) ;
374
+ clearTimeout ( timeout ) ;
375
+ resolve ( ) ;
376
+ }
377
+ } ) ;
378
+
379
+ this . adbProcess . catch ( ( error ) => {
380
+ clearTimeout ( timeout ) ;
381
+ reject ( new Error ( `ADB process error: ${ error . message } ` ) ) ;
382
+ } ) ;
383
+
384
+ this . adbProcess . then ( ( ) => {
385
+ clearTimeout ( timeout ) ;
386
+ reject ( new Error ( 'ADB process ended early without valid output' ) ) ;
387
+ } ) ;
388
+ } ) ;
389
+
390
+ console . log ( os . EOL ) ;
391
+ } catch ( error ) {
392
+ console . error ( `Failed to list profiles: ${ error . message } ` ) ;
393
+ } finally {
394
+ this . _exitQlril ( ) ;
327
395
}
396
+ }
397
+
328
398
329
- const requiredArgs = {
330
- input : 'Missing input JSON file' ,
331
- lpa : 'Missing LPA tool path' ,
332
- ...( this . isTachyon ? { } : { binary : 'Missing folder path to binaries' } )
333
- } ;
334
-
335
- for ( const [ key , errorMessage ] of Object . entries ( requiredArgs ) ) {
336
- if ( ! args [ key ] ) {
337
- throw new Error ( errorMessage ) ;
399
+ _validateArgs ( args ) {
400
+ if ( ! args ?. lpa ) {
401
+ throw new Error ( 'Missing LPA tool path' ) ;
402
+ }
403
+
404
+ this . inputJson = args ?. input ;
405
+ if ( this . inputJson ) {
406
+ try {
407
+ this . inputJsonData = JSON . parse ( fs . readFileSync ( this . inputJson ) ) ;
408
+ } catch ( error ) {
409
+ throw new Error ( `Invalid JSON in input file: ${ error . message } ` ) ;
338
410
}
339
411
}
340
-
341
- this . inputJson = args . input ;
342
- this . inputJsonData = JSON . parse ( fs . readFileSync ( this . inputJson ) ) ;
343
-
344
- this . outputFolder = args . output || 'esim_loading_logs' ;
412
+
413
+ this . outputFolder = args ?. output || 'esim_loading_logs' ;
345
414
if ( ! fs . existsSync ( this . outputFolder ) ) {
346
415
fs . mkdirSync ( this . outputFolder ) ;
347
416
}
348
-
349
417
this . lpa = args . lpa ;
350
- this . binaries = args . binary ;
418
+ this . binaries = args ? .binary ;
351
419
}
352
420
353
421
@@ -367,7 +435,7 @@ module.exports = class ESimCommands extends CLICommandBase {
367
435
368
436
const equal = _ . isEqual ( _ . sortBy ( expectedIccids ) , _ . sortBy ( iccidsOnDeviceAfterDownloadFiltered ) ) ;
369
437
370
- res . details . iccidsOnDevice = iccidsOnDeviceAfterDownload ;
438
+ res . details . iccidsOnDevice = iccidsOnDeviceAfterDownloadFiltered ;
371
439
res . details . rawLogs . push ( equal ? [ 'Profiles on device match the expected profiles' ] :
372
440
[ 'Profiles on device do not match the expected profiles' ] ) ;
373
441
res . status = equal ? 'success' : 'failed' ;
@@ -814,6 +882,7 @@ module.exports = class ESimCommands extends CLICommandBase {
814
882
} ;
815
883
816
884
const profilesOnDeviceAfterEnable = await this . _listProfiles ( port ) ;
885
+ console . log ( 'Profiles on device after enable:' , profilesOnDeviceAfterEnable ) ;
817
886
const iccidString = profilesOnDeviceAfterEnable . find ( ( line ) => line . includes ( iccid ) ) ;
818
887
if ( iccidString ) {
819
888
// check that you see the string 'enabled'
0 commit comments