@nordicsemiconductor/pc-nrfconnect-shared 128.0.0 → 129.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -93,12 +93,17 @@ export class Batch {
93
93
  this.enqueueBatchOperationObject('fw-read', core, {
94
94
  ...callbacks,
95
95
  onTaskEnd: (taskEnd: TaskEnd<DeviceBuffer>) => {
96
- if (taskEnd.result === 'success' && taskEnd.data)
96
+ if (taskEnd.result === 'success' && taskEnd.data) {
97
+ const data = Buffer.from(taskEnd.data.buffer, 'base64');
97
98
  callbacks?.onTaskEnd?.({
98
99
  ...taskEnd,
99
- data: Buffer.from(taskEnd.data.buffer, 'base64'),
100
+ task: {
101
+ ...taskEnd.task,
102
+ data,
103
+ },
104
+ data,
100
105
  });
101
- else {
106
+ } else {
102
107
  callbacks?.onException?.(new Error('Read failed'));
103
108
  }
104
109
  },
@@ -263,7 +268,7 @@ export class Batch {
263
268
 
264
269
  const sandbox = await getDeviceSandbox();
265
270
  try {
266
- await sandbox.execSubcommand<unknown>(
271
+ await sandbox.spawnNrfutilSubcommand<unknown>(
267
272
  'x-execute-batch',
268
273
  [
269
274
  '--serial-number',
@@ -12,6 +12,7 @@ import {
12
12
  import { getNrfutilLogger } from '../nrfutilLogger';
13
13
  import sandbox, { type NrfutilSandbox } from '../sandbox';
14
14
  import { Progress } from '../sandboxTypes';
15
+ import logLibVersions from './logLibVersions';
15
16
 
16
17
  export const deviceTraitsToArgs = (traits: DeviceTraits) => {
17
18
  const args: string[] = [];
@@ -171,6 +172,7 @@ export const getDeviceSandbox = async () => {
171
172
  undefined
172
173
  );
173
174
  deviceSandbox = await promiseDeviceSandbox;
175
+ deviceSandbox.getModuleVersion().then(logLibVersions);
174
176
 
175
177
  deviceSandbox.onLogging((evt, pid) => {
176
178
  const deviceLogger = getNrfutilLogger();
@@ -68,7 +68,7 @@ export default async (
68
68
  };
69
69
 
70
70
  const sandbox = await getDeviceSandbox();
71
- return sandbox.execBackgroundSubcommand<HotplugEvent>('list', args, {
71
+ return sandbox.spawnBackgroundSubcommand<HotplugEvent>('list', args, {
72
72
  onData,
73
73
  onError,
74
74
  });
@@ -7,13 +7,13 @@
7
7
  import { spawn } from 'child_process';
8
8
  import os from 'os';
9
9
 
10
- import NrfutilDeviceLib from '../../nrfutil/device/device';
10
+ import logger from '../../src/logging';
11
11
  import {
12
12
  describeVersion,
13
+ getExpectedVersion,
13
14
  resolveModuleVersion,
14
- } from '../../nrfutil/moduleVersion';
15
- import { SubDependency } from '../../nrfutil/sandboxTypes';
16
- import logger from '../logging';
15
+ } from '../moduleVersion';
16
+ import { ModuleVersion, SubDependency } from '../sandboxTypes';
17
17
 
18
18
  const log = (description: string, moduleVersion?: SubDependency | string) => {
19
19
  if (moduleVersion == null) {
@@ -67,15 +67,29 @@ const checkJLinkArchitectureOnDarwin = async () => {
67
67
  return 'arm';
68
68
  };
69
69
 
70
- export default async () => {
70
+ export default async (moduleVersion: ModuleVersion) => {
71
71
  try {
72
- const moduleVersion = await NrfutilDeviceLib.getModuleVersion();
73
72
  const dependencies = moduleVersion.dependencies;
74
73
 
75
74
  log('nrfutil-device', moduleVersion.version);
76
75
  log('nrf-device-lib', resolveModuleVersion('nrfdl', dependencies));
77
76
  log('nrfjprog DLL', resolveModuleVersion('jprog', dependencies));
78
77
  log('JLink', resolveModuleVersion('JlinkARM', dependencies));
78
+
79
+ const jlinkVersion = resolveModuleVersion('JlinkARM', dependencies);
80
+
81
+ if (jlinkVersion) {
82
+ const result = getExpectedVersion(jlinkVersion);
83
+ if (!result.isExpectedVersion) {
84
+ logger.warn(
85
+ `Installed JLink version does not match the expected version (${result.expectedVersion})`
86
+ );
87
+ }
88
+ } else {
89
+ logger.warn(
90
+ `JLink is not installed. Please install JLink from: https://www.segger.com/downloads/jlink`
91
+ );
92
+ }
79
93
  if (
80
94
  process.platform === 'darwin' &&
81
95
  os.cpus()[0].model.includes('Apple')
@@ -4,10 +4,11 @@
4
4
  * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
5
  */
6
6
 
7
- import { spawn } from 'child_process';
7
+ import { exec, spawn } from 'child_process';
8
8
  import fs from 'fs';
9
9
  import os from 'os';
10
10
  import path from 'path';
11
+ import treeKill from 'tree-kill';
11
12
 
12
13
  import describeError from '../src/logging/describeError';
13
14
  import usageData from '../src/utils/usageData';
@@ -136,7 +137,7 @@ export class NrfutilSandbox {
136
137
  };
137
138
 
138
139
  public getModuleVersion = async () => {
139
- const results = await this.execNrfutil<ModuleVersion>(this.module, [
140
+ const results = await this.spawnNrfutil<ModuleVersion>(this.module, [
140
141
  '--version',
141
142
  ]);
142
143
 
@@ -175,7 +176,7 @@ export class NrfutilSandbox {
175
176
  getNrfutilLogger()?.info(
176
177
  `Preparing nrfutil-${this.module} version: ${this.version}`
177
178
  );
178
- await this.execNrfutil(
179
+ await this.spawnNrfutil(
179
180
  'install',
180
181
  [`${this.module}=${this.version}`, '--force'],
181
182
  onProgress
@@ -193,13 +194,59 @@ export class NrfutilSandbox {
193
194
  }
194
195
  };
195
196
 
196
- private execNrfutil = async <Result>(
197
+ public getNrfutilExePath = () => path.join(this.baseDir, 'nrfutil');
198
+
199
+ public spawnNrfutilSubcommand = <Result>(
200
+ command: string,
201
+ args: string[],
202
+ onProgress?: (progress: Progress, task?: Task) => void,
203
+ onTaskBegin?: (taskBegin: TaskBegin) => void,
204
+ onTaskEnd?: (taskEnd: TaskEnd<Result>) => void,
205
+ controller?: AbortController,
206
+ editEnv?: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv
207
+ ) =>
208
+ this.spawnNrfutil<Result>(
209
+ this.module,
210
+ [command, ...args],
211
+ onProgress,
212
+ onTaskBegin,
213
+ onTaskEnd,
214
+ controller,
215
+ editEnv
216
+ );
217
+
218
+ private spawnNrfutilCommand = (
219
+ command: string,
220
+ args: string[],
221
+ parser: (data: Buffer, pid?: number) => Buffer | undefined,
222
+ onStdError: (data: Buffer, pid?: number) => void,
223
+ controller?: AbortController,
224
+ editEnv?: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv
225
+ ) =>
226
+ this.spawnCommand(
227
+ this.getNrfutilExePath(),
228
+ [
229
+ command,
230
+ ...args,
231
+ '--json',
232
+ '--log-output=stdout',
233
+ '--log-level',
234
+ this.logLevel,
235
+ ],
236
+ parser,
237
+ onStdError,
238
+ controller,
239
+ editEnv
240
+ );
241
+
242
+ private spawnNrfutil = async <Result>(
197
243
  command: string,
198
244
  args: string[],
199
245
  onProgress?: (progress: Progress, task?: Task) => void,
200
246
  onTaskBegin?: (taskBegin: TaskBegin) => void,
201
247
  onTaskEnd?: (taskEnd: TaskEnd<Result>) => void,
202
- controller?: AbortController
248
+ controller?: AbortController,
249
+ editEnv?: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv
203
250
  ) => {
204
251
  const info: Result[] = [];
205
252
  const taskEnd: TaskEnd<Result>[] = [];
@@ -207,7 +254,7 @@ export class NrfutilSandbox {
207
254
  let pid: number | undefined;
208
255
 
209
256
  try {
210
- await this.execCommand(
257
+ await this.spawnNrfutilCommand(
211
258
  command,
212
259
  args,
213
260
  (data, processId) =>
@@ -235,7 +282,8 @@ export class NrfutilSandbox {
235
282
  pid = processId;
236
283
  stdErr = (stdErr ?? '') + data.toString();
237
284
  },
238
- controller
285
+ controller,
286
+ editEnv
239
287
  );
240
288
 
241
289
  if (
@@ -276,47 +324,201 @@ export class NrfutilSandbox {
276
324
  }
277
325
  };
278
326
 
279
- public execSubcommand = <Result>(
327
+ public spawnCommand = (
328
+ command: string,
329
+ args: string[],
330
+ parser: (data: Buffer, pid?: number) => Buffer | undefined,
331
+ onStdError: (data: Buffer, pid?: number) => void,
332
+ controller?: AbortController,
333
+ editEnv: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv = env => env
334
+ ) =>
335
+ new Promise<void>((resolve, reject) => {
336
+ let aborting = false;
337
+ usageData.sendUsageData(`running nrfutil ${this.module}`, {
338
+ args,
339
+ exec: command,
340
+ });
341
+ const nrfutil = spawn(command, args, {
342
+ env: editEnv(this.env),
343
+ });
344
+
345
+ const listener = () => {
346
+ getNrfutilLogger()?.info(
347
+ `Aborting ongoing nrfutil ${
348
+ this.module
349
+ } ${command} ${JSON.stringify(args)}`
350
+ );
351
+ aborting = true;
352
+ if (nrfutil.pid) {
353
+ treeKill(nrfutil.pid);
354
+ } else {
355
+ nrfutil.kill('SIGINT');
356
+ }
357
+ };
358
+
359
+ controller?.signal.addEventListener('abort', listener);
360
+
361
+ let buffer = Buffer.from('');
362
+
363
+ nrfutil.stdout.on('data', (data: Buffer) => {
364
+ if (controller?.signal.aborted) return;
365
+
366
+ buffer = Buffer.concat([buffer, data]);
367
+ const remainingBytes = parser(buffer, nrfutil.pid);
368
+ if (remainingBytes) {
369
+ buffer = remainingBytes;
370
+ } else {
371
+ buffer = Buffer.from('');
372
+ }
373
+ });
374
+
375
+ nrfutil.stderr.on('data', (data: Buffer) => {
376
+ onStdError(data, nrfutil.pid);
377
+ });
378
+
379
+ nrfutil.on('close', code => {
380
+ controller?.signal.removeEventListener('abort', listener);
381
+ if (aborting) {
382
+ reject(
383
+ new Error(
384
+ `Aborted ongoing nrfutil ${command} ${
385
+ args[0] ?? ''
386
+ }`
387
+ )
388
+ );
389
+ return;
390
+ }
391
+
392
+ if (code === 0) {
393
+ resolve();
394
+ } else {
395
+ reject(new Error(`Failed with exit code ${code}.`));
396
+ }
397
+ });
398
+ });
399
+
400
+ public execNrfutilSubcommand = <Result>(
280
401
  command: string,
281
402
  args: string[],
282
403
  onProgress?: (progress: Progress, task?: Task) => void,
283
404
  onTaskBegin?: (taskBegin: TaskBegin) => void,
284
405
  onTaskEnd?: (taskEnd: TaskEnd<Result>) => void,
285
- controller?: AbortController
406
+ controller?: AbortController,
407
+ editEnv?: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv
286
408
  ) =>
287
- this.execNrfutil<Result>(
409
+ this.execNrfutilCommand<Result>(
288
410
  this.module,
289
411
  [command, ...args],
290
412
  onProgress,
291
413
  onTaskBegin,
292
414
  onTaskEnd,
293
- controller
415
+ controller,
416
+ editEnv
294
417
  );
295
418
 
296
- private execCommand = (
419
+ private execNrfutilCommand = async <Result>(
297
420
  command: string,
298
421
  args: string[],
299
- parser: (data: Buffer, pid?: number) => Buffer | undefined,
422
+ onProgress?: (progress: Progress, task?: Task) => void,
423
+ onTaskBegin?: (taskBegin: TaskBegin) => void,
424
+ onTaskEnd?: (taskEnd: TaskEnd<Result>) => void,
425
+ controller?: AbortController,
426
+ editEnv?: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv
427
+ ) => {
428
+ const info: Result[] = [];
429
+ const taskEnd: TaskEnd<Result>[] = [];
430
+ let stdErr: string | undefined;
431
+ let pid: number | undefined;
432
+
433
+ try {
434
+ await this.execCommand(
435
+ command,
436
+ args,
437
+ (data, processId) =>
438
+ commonParser<Result>(
439
+ data,
440
+ {
441
+ onProgress,
442
+ onTaskBegin,
443
+ onTaskEnd: end => {
444
+ taskEnd.push(end);
445
+ onTaskEnd?.(end);
446
+ },
447
+ onInfo: i => {
448
+ info.push(i);
449
+ },
450
+ onLogging: logging => {
451
+ this.onLoggingHandlers.forEach(onLogging => {
452
+ onLogging(logging, processId);
453
+ });
454
+ },
455
+ },
456
+ processId
457
+ ),
458
+ (data, processId) => {
459
+ pid = processId;
460
+ stdErr = (stdErr ?? '') + data.toString();
461
+ },
462
+ controller,
463
+ editEnv
464
+ );
465
+
466
+ if (
467
+ stdErr ||
468
+ taskEnd.filter(end => end.result === 'fail').length > 0
469
+ )
470
+ throw new Error('Task failed.');
471
+
472
+ return { taskEnd, info };
473
+ } catch (e) {
474
+ const error = e as Error;
475
+
476
+ const addPunctuation = (str: string) =>
477
+ str.endsWith('.') ? str.trim() : `${str.trim()}.`;
478
+
479
+ if (stdErr) {
480
+ error.message += `\n${addPunctuation(stdErr)}`;
481
+ }
482
+
483
+ const taskEndErrorMsg = taskEnd
484
+ .filter(end => end.result === 'fail' && !!end.message)
485
+ .map(end =>
486
+ end.message ? `Message: ${addPunctuation(end.message)}` : ''
487
+ )
488
+ .join('\n');
489
+
490
+ if (taskEndErrorMsg) {
491
+ error.message += `\n${taskEndErrorMsg}`;
492
+ }
493
+
494
+ error.message = error.message.replaceAll('Error: ', '');
495
+ usageData.sendErrorReport(
496
+ `${
497
+ pid && this.logLevel === 'trace' ? `[PID:${pid}] ` : ''
498
+ }${describeError(error)}`
499
+ );
500
+ throw error;
501
+ }
502
+ };
503
+
504
+ public execCommand = (
505
+ command: string,
506
+ args: string[],
507
+ onData: (data: Buffer, pid?: number) => void,
300
508
  onStdError: (data: Buffer, pid?: number) => void,
301
- controller?: AbortController
509
+ controller?: AbortController,
510
+ editEnv: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv = env => env
302
511
  ) =>
303
512
  new Promise<void>((resolve, reject) => {
304
513
  let aborting = false;
305
- usageData.sendUsageData(`running nrfutil ${command}`, { args });
306
- const nrfutil = spawn(
307
- path.join(this.baseDir, 'nrfutil'),
308
- [
309
- command,
310
- ...args,
311
- '--json',
312
- '--log-output=stdout',
313
- '--log-level',
314
- this.logLevel,
315
- ],
316
- {
317
- env: this.env,
318
- }
319
- );
514
+ usageData.sendUsageData(`running nrfutil ${this.module}`, {
515
+ args,
516
+ exec: command,
517
+ });
518
+
519
+ const nrfutil = exec(`${command} ${args.join(' ')}`, {
520
+ env: editEnv(this.env),
521
+ });
320
522
 
321
523
  const listener = () => {
322
524
  getNrfutilLogger()?.info(
@@ -325,26 +527,22 @@ export class NrfutilSandbox {
325
527
  } ${command} ${JSON.stringify(args)}`
326
528
  );
327
529
  aborting = true;
328
- nrfutil.kill('SIGINT');
530
+ if (nrfutil.pid) {
531
+ treeKill(nrfutil.pid);
532
+ } else {
533
+ nrfutil.kill('SIGINT');
534
+ }
329
535
  };
330
536
 
331
537
  controller?.signal.addEventListener('abort', listener);
332
538
 
333
- let buffer = Buffer.from('');
334
-
335
- nrfutil.stdout.on('data', (data: Buffer) => {
539
+ nrfutil.stdout?.on('data', (data: Buffer) => {
336
540
  if (controller?.signal.aborted) return;
337
541
 
338
- buffer = Buffer.concat([buffer, data]);
339
- const remainingBytes = parser(buffer, nrfutil.pid);
340
- if (remainingBytes) {
341
- buffer = remainingBytes;
342
- } else {
343
- buffer = Buffer.from('');
344
- }
542
+ onData(data, nrfutil.pid);
345
543
  });
346
544
 
347
- nrfutil.stderr.on('data', (data: Buffer) => {
545
+ nrfutil.stderr?.on('data', (data: Buffer) => {
348
546
  onStdError(data, nrfutil.pid);
349
547
  });
350
548
 
@@ -369,16 +567,17 @@ export class NrfutilSandbox {
369
567
  });
370
568
  });
371
569
 
372
- public execBackgroundSubcommand = <Result>(
570
+ public spawnBackgroundSubcommand = <Result>(
373
571
  command: string,
374
572
  args: string[],
375
- processors: BackgroundTask<Result>
573
+ processors: BackgroundTask<Result>,
574
+ editEnv?: (env: NodeJS.ProcessEnv) => NodeJS.ProcessEnv
376
575
  ) => {
377
576
  const controller = new AbortController();
378
577
  let running = true;
379
578
  const closedHandlers: ((error?: Error) => void)[] = [];
380
579
 
381
- const operation = this.execCommand(
580
+ const operation = this.spawnNrfutilCommand(
382
581
  this.module,
383
582
  [command, ...args],
384
583
  (data, pid) => {
@@ -400,7 +599,8 @@ export class NrfutilSandbox {
400
599
  (data, pid) => {
401
600
  processors.onError(new Error(data.toString()), pid);
402
601
  },
403
- controller
602
+ controller,
603
+ editEnv
404
604
  );
405
605
 
406
606
  operation
@@ -454,7 +654,7 @@ export class NrfutilSandbox {
454
654
  controller?: AbortController,
455
655
  args: string[] = []
456
656
  ) => {
457
- const results = await this.execSubcommand<T>(
657
+ const results = await this.spawnNrfutilSubcommand<T>(
458
658
  command,
459
659
  args,
460
660
  onProgress,
@@ -464,7 +664,7 @@ export class NrfutilSandbox {
464
664
  );
465
665
 
466
666
  if (results.taskEnd.length === 1) {
467
- return results.taskEnd[0].data;
667
+ return results.taskEnd[0].data ?? results.taskEnd[0].task.data;
468
668
  }
469
669
  throw new Error('Unexpected result');
470
670
  };
@@ -474,7 +674,7 @@ export class NrfutilSandbox {
474
674
  controller?: AbortController,
475
675
  args: string[] = []
476
676
  ) => {
477
- const results = await this.execSubcommand<T>(
677
+ const results = await this.spawnNrfutilSubcommand<T>(
478
678
  command,
479
679
  args,
480
680
  undefined,
@@ -17,13 +17,11 @@ export type FeatureClassification =
17
17
  | 'nrf-external-confidential'
18
18
  | 'nrf-external';
19
19
 
20
- export type Task = {
20
+ export type Task<T = undefined | null> = {
21
21
  id: string;
22
22
  description: string;
23
23
  name: string;
24
- data: {
25
- serialNumber: string;
26
- };
24
+ data: T;
27
25
  };
28
26
 
29
27
  export type TaskError = {
@@ -35,12 +33,11 @@ export type TaskBegin = {
35
33
  task: Task;
36
34
  };
37
35
 
38
- export type TaskEnd<T = void> = {
39
- task: Task;
36
+ export type TaskEnd<T = undefined | null> = {
37
+ task: Task<T>;
40
38
  message?: string;
41
39
  result?: 'success' | 'fail';
42
40
  error?: TaskError;
43
- name: string;
44
41
  data?: T;
45
42
  };
46
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nordicsemiconductor/pc-nrfconnect-shared",
3
- "version": "128.0.0",
3
+ "version": "129.0.0",
4
4
  "description": "Shared commodities for developing pc-nrfconnect-* packages",
5
5
  "repository": {
6
6
  "type": "git",
@@ -115,6 +115,7 @@
115
115
  "shasum": "1.0.2",
116
116
  "systeminformation": "5.17.12",
117
117
  "tailwindcss": "3.3.2",
118
+ "tree-kill-promise": "^3.0.14",
118
119
  "ts-node": "10.9.1",
119
120
  "typescript": "4.9.5",
120
121
  "util": "0.12.5",
@@ -4,14 +4,8 @@
4
4
  * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
5
  */
6
6
 
7
- import NrfutilDeviceLib from '../../nrfutil/device/device';
8
- import {
9
- getExpectedVersion,
10
- resolveModuleVersion,
11
- } from '../../nrfutil/moduleVersion';
12
7
  import appDetails from '../utils/appDetails';
13
8
  import { getAppDataDir } from '../utils/appDirs';
14
- import logLibVersions from '../utils/logLibVersions';
15
9
  import udevInstalled from '../utils/udevInstalled';
16
10
  import logger from '.';
17
11
 
@@ -20,7 +14,6 @@ export default async () => {
20
14
  if (initialMessagesSent) return;
21
15
  initialMessagesSent = true;
22
16
 
23
- logLibVersions();
24
17
  logger.debug(`Application data folder: ${getAppDataDir()}`);
25
18
 
26
19
  const {
@@ -44,23 +37,6 @@ export default async () => {
44
37
  logger.debug(`HomeDir: ${homeDir}`);
45
38
  logger.debug(`TmpDir: ${tmpDir}`);
46
39
 
47
- const dependencies = (await NrfutilDeviceLib.getModuleVersion())
48
- .dependencies;
49
- const jlinkVersion = resolveModuleVersion('JlinkARM', dependencies);
50
-
51
- if (jlinkVersion) {
52
- const result = getExpectedVersion(jlinkVersion);
53
- if (!result.isExpectedVersion) {
54
- logger.warn(
55
- `Installed JLink version does not match the expected version (${result.expectedVersion})`
56
- );
57
- }
58
- } else {
59
- logger.warn(
60
- `JLink is not installed. Please install JLink from: https://www.segger.com/downloads/jlink`
61
- );
62
- }
63
-
64
40
  if (!udevInstalled()) {
65
41
  logger.warn(
66
42
  'Required component nrf-udev is not detected. Install it from https://github.com/NordicSemiconductor/nrf-udev and restart the application'
@@ -23,6 +23,7 @@ import { Device } from '../Device/deviceSlice';
23
23
  import logger from '../logging';
24
24
  import { getAppDataDir } from './appDirs';
25
25
  import { openFile } from './open';
26
+ import { packageJsonApp } from './packageJson';
26
27
 
27
28
  const generalInfoReport = async () => {
28
29
  const [
@@ -50,10 +51,10 @@ const generalInfoReport = async () => {
50
51
  si.fsSize(),
51
52
  ]);
52
53
 
53
- const moduleVersion = await NrfutilDeviceLib.getModuleVersion();
54
- const dependencies = moduleVersion.dependencies;
54
+ const jsonApp = packageJsonApp();
55
+ const hasDeviceLib = !!jsonApp.nrfConnectForDesktop.nrfutil?.device;
55
56
 
56
- return [
57
+ const result = [
57
58
  `- System: ${manufacturer} ${model}`,
58
59
  `- BIOS: ${vendor} ${version}`,
59
60
  `- CPU: ${processors} x ${cpuManufacturer} ${brand} ${speed} GHz` +
@@ -71,15 +72,27 @@ const generalInfoReport = async () => {
71
72
  ` - node: ${node}`,
72
73
  ` - python: ${python}`,
73
74
  ` - python3: ${python3}`,
74
- ` - nrfutil-device: ${moduleVersion.version}`,
75
- ` - nrfjprog DLL: ${describeVersion(
76
- resolveModuleVersion('jprog', dependencies)
77
- )}`,
78
- ` - JLink: ${describeVersion(
79
- resolveModuleVersion('JlinkARM', dependencies)
80
- )}`,
81
- '',
82
75
  ];
76
+
77
+ if (hasDeviceLib) {
78
+ const moduleVersion = await NrfutilDeviceLib.getModuleVersion();
79
+ const dependencies = moduleVersion.dependencies;
80
+
81
+ result.push(
82
+ ...[
83
+ ` - nrfutil-device: ${moduleVersion.version}`,
84
+ ` - nrfjprog DLL: ${describeVersion(
85
+ resolveModuleVersion('jprog', dependencies)
86
+ )}`,
87
+ ` - JLink: ${describeVersion(
88
+ resolveModuleVersion('JlinkARM', dependencies)
89
+ )}`,
90
+ '',
91
+ ]
92
+ );
93
+ }
94
+
95
+ return result;
83
96
  };
84
97
 
85
98
  const allDevicesReport = (allDevices: Device[] = []) => [