@onekeyfe/hd-core 1.2.0-alpha.0 → 1.2.0-alpha.1

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.
Files changed (84) hide show
  1. package/__tests__/protocol-v2.test.ts +147 -264
  2. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  3. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  4. package/dist/api/FirmwareUpdateV4.d.ts +2 -2
  5. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  6. package/dist/api/GetDeviceInfo.d.ts.map +1 -1
  7. package/dist/api/GetFeatures.d.ts +1 -1
  8. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  9. package/dist/api/GetPassphraseState.d.ts +4 -4
  10. package/dist/api/device/DeviceUnlock.d.ts +1 -1
  11. package/dist/api/firmware/bootloaderHelper.d.ts.map +1 -1
  12. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  13. package/dist/api/protocol-v2/helpers.d.ts +2 -3
  14. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  15. package/dist/constants/index.d.ts +2 -1
  16. package/dist/constants/index.d.ts.map +1 -1
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  19. package/dist/device/Device.d.ts +11 -15
  20. package/dist/device/Device.d.ts.map +1 -1
  21. package/dist/deviceProfile/buildDeviceFeatures.d.ts +6 -0
  22. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -0
  23. package/dist/deviceProfile/buildDeviceProfile.d.ts +3 -4
  24. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
  25. package/dist/deviceProfile/index.d.ts +1 -1
  26. package/dist/deviceProfile/index.d.ts.map +1 -1
  27. package/dist/index.d.ts +579 -502
  28. package/dist/index.js +881 -931
  29. package/dist/protocols/protocol-v2/features.d.ts +2 -2
  30. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  31. package/dist/types/api/getDeviceInfo.d.ts +2 -2
  32. package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
  33. package/dist/types/api/getPassphraseState.d.ts +4 -4
  34. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  35. package/dist/types/api/protocolV2.d.ts +3 -3
  36. package/dist/types/api/protocolV2.d.ts.map +1 -1
  37. package/dist/types/device.d.ts +87 -8
  38. package/dist/types/device.d.ts.map +1 -1
  39. package/dist/types/settings.d.ts +1 -0
  40. package/dist/types/settings.d.ts.map +1 -1
  41. package/dist/utils/capabilitieUtils.d.ts.map +1 -1
  42. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  43. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  44. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  45. package/dist/utils/findDefectiveBatchDevice.d.ts +1 -1
  46. package/dist/utils/patch.d.ts +1 -1
  47. package/dist/utils/patch.d.ts.map +1 -1
  48. package/package.json +4 -4
  49. package/src/api/FirmwareUpdateV2.ts +9 -2
  50. package/src/api/FirmwareUpdateV3.ts +2 -1
  51. package/src/api/FirmwareUpdateV4.ts +24 -31
  52. package/src/api/GetDeviceInfo.ts +6 -13
  53. package/src/api/GetOnekeyFeatures.ts +3 -14
  54. package/src/api/GetPassphraseState.ts +4 -4
  55. package/src/api/firmware/bootloaderHelper.ts +3 -2
  56. package/src/api/firmware/getBinary.ts +1 -1
  57. package/src/api/firmware/releaseHelper.ts +3 -3
  58. package/src/api/firmware/uploadFirmware.ts +5 -2
  59. package/src/api/protocol-v2/DeviceReboot.ts +3 -3
  60. package/src/api/protocol-v2/helpers.ts +1 -26
  61. package/src/constants/index.ts +10 -1
  62. package/src/core/index.ts +5 -7
  63. package/src/data/messages/messages-protocol-v2.json +329 -323
  64. package/src/data-manager/DataManager.ts +4 -4
  65. package/src/data-manager/connectSettings.ts +6 -0
  66. package/src/device/Device.ts +86 -241
  67. package/src/device/DevicePool.ts +9 -9
  68. package/src/deviceProfile/buildDeviceFeatures.ts +368 -0
  69. package/src/deviceProfile/buildDeviceProfile.ts +101 -155
  70. package/src/deviceProfile/index.ts +4 -1
  71. package/src/protocols/protocol-v2/features.ts +14 -16
  72. package/src/types/api/getDeviceInfo.ts +2 -2
  73. package/src/types/api/getPassphraseState.ts +4 -4
  74. package/src/types/api/protocolV2.ts +2 -3
  75. package/src/types/device.ts +97 -34
  76. package/src/types/settings.ts +5 -0
  77. package/src/utils/capabilitieUtils.ts +1 -2
  78. package/src/utils/deviceFeaturesUtils.ts +11 -17
  79. package/src/utils/deviceInfoUtils.ts +11 -29
  80. package/src/utils/deviceVersionUtils.ts +7 -25
  81. package/src/utils/findDefectiveBatchDevice.ts +6 -6
  82. package/dist/deviceProfile/legacyFeaturesView.d.ts +0 -5
  83. package/dist/deviceProfile/legacyFeaturesView.d.ts.map +0 -1
  84. package/src/deviceProfile/legacyFeaturesView.ts +0 -123
@@ -51,7 +51,7 @@ import {
51
51
  } from '../src/protocols/protocol-v2/features';
52
52
  import {
53
53
  buildProfileFromProtocolV2,
54
- buildProtocolV2GetFeaturesPayload,
54
+ buildProtocolV2FeaturesPayload,
55
55
  } from '../src/deviceProfile';
56
56
  import {
57
57
  getDeviceType,
@@ -91,44 +91,34 @@ const descriptor = {
91
91
  */
92
92
  function stubDevice<T extends Record<string, any>>(device: T): T {
93
93
  const d = device as any;
94
- d.isProtocolV2 ??= () =>
95
- d.profile?.protocol === 'V2' || d.originalDescriptor?.protocolType === 'V2';
94
+ d.isProtocolV2 ??= () => d.originalDescriptor?.protocolType === 'V2';
96
95
  d.getProtocol ??= () => (d.isProtocolV2() ? 'V2' : 'V1');
97
- d.getCurrentDeviceType ??= () => d.profile?.deviceType ?? getDeviceType(d.features);
98
- d.getCurrentFirmwareType ??= () => d.profile?.firmwareType ?? getFirmwareType(d.features);
96
+ d.getCurrentDeviceType ??= () => getDeviceType(d.features);
97
+ d.getCurrentFirmwareType ??= () => getFirmwareType(d.features);
99
98
  d.getCurrentFirmwareVersionString ??= () =>
100
- d.profile?.versions?.firmware ??
101
- (d.features ? getDeviceFirmwareVersion(d.features).join('.') : undefined);
102
- d.getCurrentBLEFirmwareVersionString ??= () => d.profile?.versions?.ble ?? undefined;
103
- d.getCurrentSafetyChecks ??= () => d.features?.safety_checks;
104
- d.getCurrentDeviceId ??= () => d.profile?.deviceId || d.features?.device_id || undefined;
105
- d.getCurrentSerialNo ??= () => d.profile?.serialNo || '';
99
+ d.features ? getDeviceFirmwareVersion(d.features).join('.') : undefined;
100
+ d.getCurrentBLEFirmwareVersionString ??= () => d.features?.bleVersion;
101
+ d.getCurrentSafetyChecks ??= () => d.features?.safetyChecks;
102
+ d.getCurrentDeviceId ??= () => d.features?.deviceId || undefined;
103
+ d.getCurrentSerialNo ??= () => d.features?.serialNo || '';
106
104
  d.getCurrentPassphraseProtection ??= () =>
107
- d.profile ? d.profile.status?.passphraseProtection : d.features?.passphrase_protection;
105
+ d.features?.passphraseProtection;
108
106
  d.getCurrentMethodVersionRange ??= (fn: (model: any) => any) => {
109
107
  const deviceType = d.getCurrentDeviceType();
110
108
  const range = fn(deviceType);
111
109
  if (range) return range;
112
110
  return getMethodVersionRange(d.features, fn);
113
111
  };
114
- d.updateProfile ??= (p: any) => {
115
- d.profile = p;
116
- };
117
- d.applyProfileUpdate ??= (p: any) => {
118
- d.profile = p;
119
- return p;
112
+ d.updateProtocolV2Features ??= (deviceInfo?: ProtocolV2DeviceInfo) => {
113
+ d.features = buildProtocolV2FeaturesPayload(deviceInfo, d.features);
114
+ return d.features;
120
115
  };
121
116
  return device;
122
117
  }
123
118
 
124
119
  // 直接复用生产映射函数,避免测试内副本与实现漂移(之前的手抄副本已缺失 se boot 字段)
125
120
  function normalizeProtocolV2Features(_descriptor: unknown, deviceInfo?: ProtocolV2DeviceInfo) {
126
- const profile = buildProfileFromProtocolV2({
127
- deviceInfo,
128
- sources: ['deviceInfo'],
129
- scope: 'verify',
130
- });
131
- return buildProtocolV2GetFeaturesPayload(profile, deviceInfo);
121
+ return buildProtocolV2FeaturesPayload(deviceInfo);
132
122
  }
133
123
 
134
124
  async function requestProtocolV2LegacyFeatures({
@@ -152,15 +142,13 @@ describe('Protocol V2 feature adapter', () => {
152
142
  });
153
143
  expect(typedCall).not.toHaveBeenCalled();
154
144
  expect(deviceInfo).toEqual(buildMockProtocolV2DeviceInfo());
155
- // 空 serial_no + fallbackSerialNo:身份字段回退 transport path
156
145
  const profile = buildProfileFromProtocolV2({
157
146
  deviceInfo,
158
- fallbackSerialNo: 'usb-path',
159
147
  });
160
148
  expect(profile).toMatchObject({
161
149
  protocol: 'V2',
162
- deviceId: 'usb-path',
163
- serialNo: 'usb-path',
150
+ deviceId: '',
151
+ serialNo: '',
164
152
  status: { initialized: true },
165
153
  versions: { firmware: '0.1.0' },
166
154
  });
@@ -174,6 +162,7 @@ describe('Protocol V2 feature adapter', () => {
174
162
  protocol_version: 1,
175
163
  hw: {
176
164
  serial_no: 'PR2SERIAL',
165
+ device_id: 'PRO2-DEVICE-ID',
177
166
  },
178
167
  fw: {
179
168
  board: {
@@ -223,40 +212,34 @@ describe('Protocol V2 feature adapter', () => {
223
212
  },
224
213
  });
225
214
 
226
- expect(features.device_id).toBe('PR2SERIAL');
227
- expect(features.serial_no).toBe('PR2SERIAL');
228
- expect(features.onekey_serial_no).toBe('PR2SERIAL');
229
- expect(features.onekey_device_type).toBe('pro2');
230
- expect(features.protocol_version).toBe(1);
231
- expect(features.major_version).toBe(1);
232
- expect(features.minor_version).toBe(2);
233
- expect(features.patch_version).toBe(3);
234
- expect(features.onekey_firmware_version).toBe('1.2.3');
235
- expect(features.onekey_firmware_build_id).toBe('app-build');
236
- expect(features.onekey_firmware_hash).toBe('abc123');
237
- expect(features.bootloader_version).toBe('0.2.0');
238
- expect(features.onekey_boot_build_id).toBe('boot-build');
239
- expect(features.onekey_boot_hash).toBe('0a0b');
240
- expect(features.onekey_board_hash).toBe('0102ff');
241
- expect(features.ble_name).toBe('Pro2 BLE');
242
- expect(features.onekey_ble_version).toBe('4.5.6');
243
- expect(features.onekey_ble_hash).toBe('0c0d');
244
- expect(features.onekey_se01_version).toBe('7.8.9');
245
- expect(features.onekey_se01_hash).toBe('0e0f');
246
- expect(features.onekey_se01_state).toBe('APP');
247
- expect(features.onekey_se02_state).toBe('BOOT');
215
+ expect(features.deviceId).toBe('PRO2-DEVICE-ID');
216
+ expect(features.serialNo).toBe('PR2SERIAL');
217
+ expect(features.deviceType).toBe('pro2');
218
+ expect(features.protocolVersion).toBe(1);
219
+ expect(features.firmwareVersion).toBe('1.2.3');
220
+ expect(features.verify?.firmwareBuildId).toBe('app-build');
221
+ expect(features.verify?.firmwareHash).toBe('abc123');
222
+ expect(features.bootloaderVersion).toBe('0.2.0');
223
+ expect(features.verify?.bootloaderBuildId).toBe('boot-build');
224
+ expect(features.verify?.bootloaderHash).toBe('0a0b');
225
+ expect(features.verify?.boardHash).toBe('0102ff');
226
+ expect(features.bleName).toBe('Pro2 BLE');
227
+ expect(features.bleVersion).toBe('4.5.6');
228
+ expect(features.verify?.bleHash).toBe('0c0d');
229
+ expect(features.se01Version).toBe('7.8.9');
230
+ expect(features.verify?.se01Hash).toBe('0e0f');
248
231
  expect(features.label).toBe('My Pro2');
249
232
  expect(features.language).toBe('en-US');
250
233
  expect(features.initialized).toBe(false);
251
- expect(features.needs_backup).toBe(true);
252
- expect(features.passphrase_protection).toBe(true);
253
- expect(features.ble_enable).toBe(true);
234
+ expect(features.backupRequired).toBe(true);
235
+ expect(features.passphraseProtection).toBe(true);
236
+ expect(features.bleEnabled).toBe(true);
254
237
  });
255
238
 
256
239
  test('uses GetPassphraseState payloads compatible with Pro series passphrase flow', async () => {
257
240
  const features = normalizeProtocolV2Features(descriptor as any);
258
241
  // Pro2 版本线独立于 Pro 系列:V2 设备无论固件版本都支持 GetPassphraseState
259
- features.onekey_firmware_version = '1.2.3';
242
+ features.firmwareVersion = '1.2.3';
260
243
  const typedCall = jest.fn().mockResolvedValue({
261
244
  type: 'PassphraseState',
262
245
  message: {
@@ -296,12 +279,12 @@ describe('Protocol V2 feature adapter', () => {
296
279
 
297
280
  test('returns unified GetPassphraseState object payload for existing Pro devices', async () => {
298
281
  const features = {
299
- device_id: 'pro-device-id',
300
- onekey_device_type: 'PRO',
301
- onekey_firmware_version: '4.15.0',
302
- passphrase_protection: true,
303
- session_id: 'feature-session',
304
- unlocked_attach_pin: true,
282
+ deviceId: 'pro-device-id',
283
+ deviceType: 'pro',
284
+ firmwareVersion: '4.15.0',
285
+ passphraseProtection: true,
286
+ sessionId: 'feature-session',
287
+ unlockedAttachPin: true,
305
288
  };
306
289
  const typedCall = jest.fn().mockResolvedValue({
307
290
  type: 'PassphraseState',
@@ -319,6 +302,7 @@ describe('Protocol V2 feature adapter', () => {
319
302
  },
320
303
  });
321
304
  method.device = stubDevice({
305
+ originalDescriptor: { ...descriptor, protocolType: 'V2' },
322
306
  features,
323
307
  commands: { typedCall },
324
308
  updateInternalState,
@@ -327,10 +311,10 @@ describe('Protocol V2 feature adapter', () => {
327
311
  }) as any;
328
312
 
329
313
  await expect(method.run()).resolves.toEqual({
330
- passphrase_state: 'state-pro',
331
- session_id: 'session-pro',
332
- unlocked_attach_pin: false,
333
- passphrase_protection: true,
314
+ passphraseState: 'state-pro',
315
+ sessionId: 'session-pro',
316
+ unlockedAttachPin: false,
317
+ passphraseProtection: true,
334
318
  });
335
319
  expect(updateInternalState).toHaveBeenCalledWith(
336
320
  true,
@@ -341,14 +325,14 @@ describe('Protocol V2 feature adapter', () => {
341
325
  );
342
326
  });
343
327
 
344
- test('prefers DeviceProfile for GetPassphraseState response metadata', async () => {
328
+ test('uses features for GetPassphraseState response metadata', async () => {
345
329
  const features = {
346
- device_id: 'legacy-pro-device-id',
347
- onekey_device_type: 'PRO',
348
- onekey_firmware_version: '4.15.0',
349
- passphrase_protection: false,
350
- session_id: 'feature-session',
351
- unlocked_attach_pin: true,
330
+ deviceId: null,
331
+ deviceType: 'pro2',
332
+ firmwareVersion: '4.15.0',
333
+ passphraseProtection: true,
334
+ sessionId: 'feature-session',
335
+ unlockedAttachPin: true,
352
336
  };
353
337
  const typedCall = jest.fn().mockResolvedValue({
354
338
  type: 'PassphraseState',
@@ -367,51 +351,26 @@ describe('Protocol V2 feature adapter', () => {
367
351
  },
368
352
  });
369
353
  method.device = stubDevice({
354
+ originalDescriptor: { ...descriptor, protocolType: 'V2' },
370
355
  features,
371
- profile: {
372
- protocol: 'V2',
373
- sources: ['deviceInfo'],
374
- deviceType: 'pro2',
375
- firmwareType: 'universal',
376
- deviceId: 'PR2SERIAL',
377
- serialNo: 'PR2SERIAL',
378
- label: null,
379
- bleName: null,
380
- status: {
381
- mode: 'normal',
382
- initialized: true,
383
- bootloaderMode: false,
384
- unlocked: null,
385
- passphraseProtection: true,
386
- backupRequired: false,
387
- noBackup: null,
388
- language: null,
389
- },
390
- versions: {
391
- firmware: '4.15.0',
392
- bootloader: null,
393
- board: null,
394
- ble: null,
395
- },
396
- },
397
356
  commands: { typedCall },
398
357
  updateInternalState,
399
358
  getFeatures,
400
359
  getCurrentDeviceType: () => 'pro2',
401
- getCurrentDeviceId: () => 'PR2SERIAL',
360
+ getCurrentDeviceId: () => undefined,
402
361
  getCurrentPassphraseProtection: () => true,
403
362
  }) as any;
404
363
 
405
364
  await expect(method.run()).resolves.toEqual({
406
- passphrase_state: 'state-pro2',
407
- session_id: 'session-pro2',
408
- unlocked_attach_pin: false,
409
- passphrase_protection: true,
365
+ passphraseState: 'state-pro2',
366
+ sessionId: 'session-pro2',
367
+ unlockedAttachPin: false,
368
+ passphraseProtection: true,
410
369
  });
411
370
  expect(getFeatures).not.toHaveBeenCalled();
412
371
  });
413
372
 
414
- test('uses DeviceProfile identity for Pro2 passphrase session cache', async () => {
373
+ test('stores Pro2 passphrase session cache without synthetic device id', async () => {
415
374
  const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
416
375
  const typedCall = jest.fn().mockResolvedValue({
417
376
  type: 'PassphraseState',
@@ -459,8 +418,8 @@ describe('Protocol V2 feature adapter', () => {
459
418
  hw: { serial_no: 'PR2SERIAL' },
460
419
  }
461
420
  );
462
- (device as any).features.onekey_firmware_version = '4.15.0';
463
- (device as any).features.passphrase_protection = true;
421
+ (device as any).features.firmwareVersion = '4.15.0';
422
+ (device as any).features.passphraseProtection = true;
464
423
  (device as any).features.unlocked = true;
465
424
  (device as any).commands = { typedCall };
466
425
 
@@ -470,8 +429,8 @@ describe('Protocol V2 feature adapter', () => {
470
429
  });
471
430
 
472
431
  expect(device.passphraseState).toBeUndefined();
473
- expect(device.features?.passphrase_protection).toBe(true);
474
- expect(device.features?.session_id).toBeNull();
432
+ expect(device.features?.passphraseProtection).toBe(true);
433
+ expect(device.features?.sessionId).toBeNull();
475
434
  expect(device.getInternalState()).toBeUndefined();
476
435
  device.passphraseState = 'state-auto';
477
436
  expect(device.getInternalState()).toBe('session-auto');
@@ -501,7 +460,7 @@ describe('Protocol V2 feature adapter', () => {
501
460
  },
502
461
  }
503
462
  );
504
- (device as any).features.onekey_firmware_version = '4.15.0';
463
+ (device as any).features.firmwareVersion = '4.15.0';
505
464
  (device as any).features.unlocked = true;
506
465
  (device as any).commands = { typedCall };
507
466
 
@@ -512,8 +471,8 @@ describe('Protocol V2 feature adapter', () => {
512
471
  newSession: 'main-pin-session',
513
472
  });
514
473
 
515
- expect(device.features?.passphrase_protection).toBe(false);
516
- expect(device.features?.session_id).toBeNull();
474
+ expect(device.features?.passphraseProtection).toBe(false);
475
+ expect(device.features?.sessionId).toBeNull();
517
476
  expect(device.getInternalState()).toBeUndefined();
518
477
  expect(typedCall).toHaveBeenLastCalledWith('GetPassphraseState', 'PassphraseState', {
519
478
  _only_main_pin: true,
@@ -535,8 +494,8 @@ describe('Protocol V2 feature adapter', () => {
535
494
  ...descriptor,
536
495
  protocolType: 'V2',
537
496
  } as any);
538
- (device as any).features.onekey_firmware_version = '4.15.0';
539
- (device as any).features.passphrase_protection = true;
497
+ (device as any).features.firmwareVersion = '4.15.0';
498
+ (device as any).features.passphraseProtection = true;
540
499
  (device as any).commands = { typedCall };
541
500
 
542
501
  await expect(device.checkPassphraseStateSafety('expected-state', false, true)).resolves.toBe(
@@ -552,15 +511,14 @@ describe('Protocol V2 feature adapter', () => {
552
511
  test('marks fallback features as unavailable when DeviceInfo is missing', () => {
553
512
  const features = normalizeProtocolV2Features(descriptor as any);
554
513
 
555
- expect(features.device_id).toBe('');
556
- expect(features.serial_no).toBe('');
557
- expect(features.onekey_serial_no).toBe('');
558
- expect(features.initialized).toBe(false);
559
- expect(features.unlocked).toBe(false);
560
- expect(features.firmware_present).toBe(false);
514
+ expect(features.deviceId).toBeNull();
515
+ expect(features.serialNo).toBe('');
516
+ expect(features.initialized).toBeNull();
517
+ expect(features.unlocked).toBeNull();
518
+ expect(features.firmwarePresent).toBeNull();
561
519
  });
562
520
 
563
- test('uses Protocol V2 DeviceInfo serial_no as device_id instead of descriptor id', () => {
521
+ test('uses Protocol V2 hw.device_id and does not fall back to serial_no', () => {
564
522
  const features = normalizeProtocolV2Features(
565
523
  {
566
524
  id: 'PR2000000000',
@@ -570,108 +528,69 @@ describe('Protocol V2 feature adapter', () => {
570
528
  {
571
529
  hw: {
572
530
  serial_no: 'PR9999999999',
531
+ device_id: 'DEVICE-ID-9999',
573
532
  },
574
533
  }
575
534
  );
576
535
 
577
- expect(features.device_id).toBe('PR9999999999');
578
- expect(features.onekey_serial_no).toBe('PR9999999999');
579
- expect(features.serial_no).toBe('PR9999999999');
536
+ expect(features.deviceId).toBe('DEVICE-ID-9999');
537
+ expect(features.serialNo).toBe('PR9999999999');
580
538
  });
581
539
 
582
- test('does not expose legacy feature ids when Protocol V2 profile is incomplete', () => {
540
+ test('does not use Protocol V2 serial_no as deviceId when hw.device_id is absent', () => {
541
+ const features = normalizeProtocolV2Features(
542
+ {
543
+ id: 'PR2000000000',
544
+ path: 'PR2000000000',
545
+ protocolType: 'V2',
546
+ } as any,
547
+ {
548
+ hw: {
549
+ serial_no: 'PR9999999999',
550
+ },
551
+ }
552
+ );
553
+
554
+ expect(features.deviceId).toBeNull();
555
+ expect(features.serialNo).toBe('PR9999999999');
556
+ });
557
+
558
+ test('uses Protocol V2 features directly when profile is absent', () => {
583
559
  const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
584
560
  (device as any).features = {
585
561
  ...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
586
- device_id: 'LEGACY-ID',
587
- serial_no: 'LEGACY-SERIAL',
562
+ serialNo: 'LEGACY-SERIAL',
588
563
  label: 'Legacy Label',
589
- ble_name: 'Legacy BLE',
590
- passphrase_protection: true,
564
+ bleName: 'Legacy BLE',
565
+ passphraseProtection: true,
591
566
  };
592
- device.updateProfile({
593
- protocol: 'V2',
594
- sources: ['deviceInfo'],
595
- deviceType: 'pro2',
596
- firmwareType: 'universal',
597
- deviceId: '',
598
- serialNo: '',
599
- label: null,
600
- bleName: null,
601
- status: {
602
- mode: 'normal',
603
- initialized: true,
604
- bootloaderMode: false,
605
- unlocked: null,
606
- passphraseProtection: null,
607
- backupRequired: false,
608
- noBackup: null,
609
- language: null,
610
- },
611
- versions: {
612
- firmware: null,
613
- bootloader: null,
614
- board: null,
615
- ble: null,
616
- },
617
- });
618
567
 
619
568
  expect(device.toMessageObject()).toMatchObject({
620
- uuid: '',
569
+ uuid: 'LEGACY-SERIAL',
621
570
  deviceId: null,
622
- bleName: null,
623
- label: 'OneKey Pro2',
571
+ bleName: 'Legacy BLE',
572
+ label: 'Legacy Label',
624
573
  deviceType: 'pro2',
625
574
  });
626
- expect(device.getCurrentPassphraseProtection()).toBeNull();
575
+ expect(device.getCurrentPassphraseProtection()).toBe(true);
627
576
  expect(device.hasUsePassphrase()).toBe(true);
628
- expect(device.isInitialized()).toBe(true);
629
- expect(device.getMode()).toBe('normal');
630
- expect(device.getFirmwareVersion()).toBeNull();
631
577
  });
632
578
 
633
- test('keeps Protocol V2 cached profile as identity source when syncing legacy features', () => {
579
+ test('syncs Protocol V2 cached features without cached profile', () => {
634
580
  const cached = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
635
581
  (cached as any).features = {
636
582
  ...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
637
- device_id: 'LEGACY-ID',
638
- serial_no: 'LEGACY-SERIAL',
639
- onekey_serial_no: 'LEGACY-SERIAL',
583
+ deviceId: null,
584
+ serialNo: 'LEGACY-SERIAL',
640
585
  };
641
- cached.updateProfile({
642
- protocol: 'V2',
643
- sources: ['deviceInfo'],
644
- deviceType: 'pro2',
645
- firmwareType: 'universal',
646
- deviceId: '',
647
- serialNo: '',
648
- label: null,
649
- bleName: null,
650
- status: {
651
- mode: 'normal',
652
- initialized: true,
653
- bootloaderMode: false,
654
- unlocked: null,
655
- passphraseProtection: null,
656
- backupRequired: false,
657
- noBackup: null,
658
- language: null,
659
- },
660
- versions: {
661
- firmware: null,
662
- bootloader: null,
663
- board: null,
664
- ble: null,
665
- },
666
- });
667
586
 
668
587
  const current = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
669
588
  current.updateFromCache(cached);
670
589
 
671
590
  expect(current.getCurrentDeviceId()).toBeUndefined();
672
- expect(current.getCurrentSerialNo()).toBe('');
591
+ expect(current.getCurrentSerialNo()).toBe('LEGACY-SERIAL');
673
592
  expect(current.toMessageObject()).toMatchObject({
674
- uuid: '',
593
+ uuid: 'LEGACY-SERIAL',
675
594
  deviceId: null,
676
595
  });
677
596
  });
@@ -695,9 +614,9 @@ describe('Protocol V2 feature adapter', () => {
695
614
  descriptor: descriptor as any,
696
615
  });
697
616
 
698
- expect(features.device_id).toBe('PR2SERIAL');
617
+ expect(features.deviceId).toBeNull();
699
618
  expect(features.initialized).toBe(true);
700
- expect(features.passphrase_protection).toBe(true);
619
+ expect(features.passphraseProtection).toBe(true);
701
620
  expect(commands.typedCall).toHaveBeenCalledTimes(1);
702
621
  expect(commands.typedCall).toHaveBeenNthCalledWith(
703
622
  1,
@@ -776,7 +695,6 @@ describe('Protocol V2 feature adapter', () => {
776
695
  },
777
696
  commands: { typedCall },
778
697
  _updateFeatures: jest.fn(),
779
- updateProfile: jest.fn(),
780
698
  });
781
699
 
782
700
  const result = await method.run();
@@ -801,7 +719,7 @@ describe('Protocol V2 feature adapter', () => {
801
719
  expect(result).toMatchObject({
802
720
  protocol: 'V2',
803
721
  deviceType: 'pro2',
804
- deviceId: 'PR2SERIAL',
722
+ deviceId: '',
805
723
  serialNo: 'PR2SERIAL',
806
724
  label: 'Raw Pro2',
807
725
  bleName: 'Raw Pro2 BLE',
@@ -846,17 +764,16 @@ describe('Protocol V2 feature adapter', () => {
846
764
  onekey_ble_version: '2.3.4',
847
765
  },
848
766
  commands: { typedCall },
849
- updateProfile: jest.fn(),
850
767
  });
851
768
 
852
769
  const result = await method.run();
853
770
 
854
771
  expect(result).toMatchObject({
855
772
  protocol: 'V2',
856
- // 身份字段不得取自 legacy features(LEGACY-ID / LEGACY-SERIAL);
857
- // hw.serial_no 缺失时回退 descriptor.path(早期工程板无 serial 的 mock 身份)。
858
- deviceId: 'usb-path',
859
- serialNo: 'usb-path',
773
+ // 身份字段不得取自 legacy features(LEGACY-ID / LEGACY-SERIAL),
774
+ // hw.serial_no 缺失时也不回退 descriptor.path
775
+ deviceId: '',
776
+ serialNo: '',
860
777
  label: null,
861
778
  bleName: null,
862
779
  status: {
@@ -948,7 +865,6 @@ describe('Protocol V2 feature adapter', () => {
948
865
  originalDescriptor: { ...descriptor, protocolType: 'V2' },
949
866
  features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
950
867
  commands: { typedCall },
951
- updateProfile: jest.fn(),
952
868
  });
953
869
 
954
870
  const result = await method.run();
@@ -1088,7 +1004,7 @@ describe('Protocol V2 feature adapter', () => {
1088
1004
  });
1089
1005
  });
1090
1006
 
1091
- test('returns Protocol V2 oneKey fields without calling legacy OnekeyGetFeatures', async () => {
1007
+ test('does not call legacy OnekeyGetFeatures for Protocol V2 devices', async () => {
1092
1008
  const method = new GetOnekeyFeatures({
1093
1009
  id: 1,
1094
1010
  payload: {
@@ -1124,17 +1040,11 @@ describe('Protocol V2 feature adapter', () => {
1124
1040
  }),
1125
1041
  expect.anything()
1126
1042
  );
1127
- expect(message).toMatchObject({
1128
- onekey_device_type: 'pro2',
1129
- onekey_firmware_version: '1.2.3',
1130
- onekey_firmware_build_id: 'app-build',
1131
- onekey_serial_no: 'PR2SERIAL',
1132
- onekey_ble_name: 'Pro2 BLE',
1133
- });
1043
+ expect(message).toEqual({});
1134
1044
  expect(message).not.toHaveProperty('label');
1135
1045
  });
1136
1046
 
1137
- test('refreshes cached Protocol V2 profile with a lightweight status request on later runs', async () => {
1047
+ test('refreshes cached Protocol V2 features with a lightweight status request on later runs', async () => {
1138
1048
  const device = Device.fromDescriptor({
1139
1049
  path: 'usb-path',
1140
1050
  protocolType: 'V2',
@@ -1169,17 +1079,17 @@ describe('Protocol V2 feature adapter', () => {
1169
1079
  await device.initialize();
1170
1080
 
1171
1081
  expect(device.features).toMatchObject({
1172
- device_id: 'PR2SERIAL',
1173
- onekey_firmware_version: '1.2.3',
1174
- passphrase_protection: false,
1082
+ deviceId: null,
1083
+ firmwareVersion: '1.2.3',
1084
+ passphraseProtection: false,
1175
1085
  label: 'renamed',
1176
1086
  });
1177
- expect(device.profile?.deviceId).toBe('PR2SERIAL');
1087
+ expect((device as any).profile).toBeUndefined();
1178
1088
  // status 字段被第二次刷新更新
1179
- expect(device.profile?.status.passphraseProtection).toBe(false);
1180
- expect(device.profile?.label).toBe('renamed');
1089
+ expect(device.features?.passphraseProtection).toBe(false);
1090
+ expect(device.features?.label).toBe('renamed');
1181
1091
  // 轻量刷新不含 fw target,已有版本信息按字段级合并保留
1182
- expect(device.profile?.versions.firmware).toBe('1.2.3');
1092
+ expect(device.features?.firmwareVersion).toBe('1.2.3');
1183
1093
  expect(typedCall).toHaveBeenCalledTimes(2);
1184
1094
  expect(typedCall).toHaveBeenNthCalledWith(
1185
1095
  1,
@@ -1253,15 +1163,15 @@ describe('Protocol V2 feature adapter', () => {
1253
1163
  const features = await device.getFeatures();
1254
1164
 
1255
1165
  expect(device.features).toMatchObject({
1256
- device_id: 'PR2SERIAL',
1257
- onekey_firmware_version: '1.2.4',
1258
- passphrase_protection: true,
1166
+ deviceId: null,
1167
+ firmwareVersion: '1.2.4',
1168
+ passphraseProtection: true,
1259
1169
  });
1260
1170
  expect(features).toMatchObject({
1261
- onekey_device_type: 'pro2',
1262
- onekey_serial_no: 'PR2SERIAL',
1263
- onekey_firmware_version: '1.2.4',
1264
- passphrase_protection: true,
1171
+ deviceType: 'pro2',
1172
+ serialNo: 'PR2SERIAL',
1173
+ firmwareVersion: '1.2.4',
1174
+ passphraseProtection: true,
1265
1175
  });
1266
1176
  expect(typedCall).toHaveBeenCalledTimes(2);
1267
1177
  expect(typedCall).toHaveBeenNthCalledWith(
@@ -1319,7 +1229,6 @@ describe('Protocol V2 feature adapter', () => {
1319
1229
  });
1320
1230
  method.init();
1321
1231
  (method as any).device = stubDevice({
1322
- profile: device.profile,
1323
1232
  features: device.features,
1324
1233
  commands: { typedCall: jest.fn() },
1325
1234
  });
@@ -1358,14 +1267,14 @@ describe('Protocol V2 feature adapter', () => {
1358
1267
  expect(typedCall).not.toHaveBeenCalledWith('GetAddress', 'Address', expect.anything());
1359
1268
  expect(typedCall).not.toHaveBeenCalledWith('GetFeatures', 'Features', {});
1360
1269
  expect(features).toMatchObject({
1361
- onekey_device_type: 'pro2',
1362
- device_id: 'PR2SERIAL',
1363
- onekey_firmware_version: '1.2.3',
1270
+ deviceType: 'pro2',
1271
+ deviceId: null,
1272
+ firmwareVersion: '1.2.3',
1364
1273
  unlocked: true,
1365
1274
  });
1366
1275
  });
1367
1276
 
1368
- test('syncs Protocol V2 profile passphrase state after unlock response', async () => {
1277
+ test('syncs Protocol V2 features passphrase state after unlock response', async () => {
1369
1278
  const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
1370
1279
  (device as any).features = normalizeProtocolV2Features(
1371
1280
  { ...descriptor, protocolType: 'V2' } as any,
@@ -1375,32 +1284,6 @@ describe('Protocol V2 feature adapter', () => {
1375
1284
  status: { passphrase_protection: false },
1376
1285
  }
1377
1286
  );
1378
- device.updateProfile({
1379
- protocol: 'V2',
1380
- sources: ['deviceInfo'],
1381
- deviceType: 'pro2',
1382
- firmwareType: 'universal',
1383
- deviceId: 'PR2SERIAL',
1384
- serialNo: 'PR2SERIAL',
1385
- label: null,
1386
- bleName: null,
1387
- status: {
1388
- mode: 'normal',
1389
- initialized: true,
1390
- bootloaderMode: false,
1391
- unlocked: null,
1392
- passphraseProtection: false,
1393
- backupRequired: false,
1394
- noBackup: null,
1395
- language: null,
1396
- },
1397
- versions: {
1398
- firmware: '4.15.0',
1399
- bootloader: null,
1400
- board: null,
1401
- ble: null,
1402
- },
1403
- });
1404
1287
  const typedCall = jest.fn().mockResolvedValue({
1405
1288
  type: 'UnLockDeviceResponse',
1406
1289
  message: {
@@ -1412,8 +1295,8 @@ describe('Protocol V2 feature adapter', () => {
1412
1295
 
1413
1296
  await device.unlockDevice();
1414
1297
 
1415
- expect(device.profile?.status.passphraseProtection).toBe(true);
1416
- expect(device.features?.passphrase_protection).toBe(true);
1298
+ expect((device as any).profile).toBeUndefined();
1299
+ expect(device.features?.passphraseProtection).toBe(true);
1417
1300
  });
1418
1301
  });
1419
1302
 
@@ -1432,7 +1315,7 @@ describe('API compatibility handling', () => {
1432
1315
  method.init();
1433
1316
  (method as any).device = stubDevice({
1434
1317
  features: {
1435
- onekey_device_type: 'pro2',
1318
+ deviceType: 'pro2',
1436
1319
  },
1437
1320
  originalDescriptor: {
1438
1321
  protocolType: 'V2',
@@ -1470,7 +1353,7 @@ describe('API compatibility handling', () => {
1470
1353
 
1471
1354
  test('does not mark Pro2 Tron, Solana, TON, SUI and Polkadot methods as unsupported', () => {
1472
1355
  const features = {
1473
- onekey_device_type: 'pro2',
1356
+ deviceType: 'pro2',
1474
1357
  } as Features;
1475
1358
 
1476
1359
  const tronMethod = new TronGetAddress({
@@ -1621,7 +1504,7 @@ describe('API compatibility handling', () => {
1621
1504
 
1622
1505
  test('allows Pro2 Solana signing methods through Protocol V2 version checks', () => {
1623
1506
  const features = {
1624
- onekey_device_type: 'pro2',
1507
+ deviceType: 'pro2',
1625
1508
  } as Features;
1626
1509
 
1627
1510
  const solSignMessageMethod = new SolSignMessage({
@@ -1715,7 +1598,7 @@ describe('API compatibility handling', () => {
1715
1598
  method.init();
1716
1599
  (method as any).device = stubDevice({
1717
1600
  features: {
1718
- onekey_device_type: 'pro2',
1601
+ deviceType: 'pro2',
1719
1602
  },
1720
1603
  originalDescriptor: {
1721
1604
  protocolType: 'V2',
@@ -2589,7 +2472,7 @@ describe('Protocol V2 reboot methods', () => {
2589
2472
  });
2590
2473
  });
2591
2474
 
2592
- test('sends DeviceReboot from deviceReboot', async () => {
2475
+ test('sends DevReboot from deviceReboot', async () => {
2593
2476
  const typedCall = jest.fn().mockResolvedValue({ message: { message: 'ok' } });
2594
2477
  const method = new DeviceReboot({
2595
2478
  id: 1,
@@ -2603,7 +2486,7 @@ describe('Protocol V2 reboot methods', () => {
2603
2486
 
2604
2487
  await method.run();
2605
2488
 
2606
- expect(typedCall).toHaveBeenCalledWith('DeviceReboot', 'Success', {
2489
+ expect(typedCall).toHaveBeenCalledWith('DevReboot', 'Success', {
2607
2490
  reboot_type: 2,
2608
2491
  });
2609
2492
  });