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

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 (68) hide show
  1. package/__tests__/protocol-v2.test.ts +56 -178
  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/device/DeviceUnlock.d.ts +1 -1
  10. package/dist/api/firmware/bootloaderHelper.d.ts.map +1 -1
  11. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  12. package/dist/api/tron/TronSignMessage.d.ts +0 -1
  13. package/dist/api/tron/TronSignMessage.d.ts.map +1 -1
  14. package/dist/api/tron/TronSignTransaction.d.ts +0 -1
  15. package/dist/api/tron/TronSignTransaction.d.ts.map +1 -1
  16. package/dist/core/index.d.ts.map +1 -1
  17. package/dist/device/Device.d.ts +10 -15
  18. package/dist/device/Device.d.ts.map +1 -1
  19. package/dist/deviceProfile/buildDeviceFeatures.d.ts +6 -0
  20. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -0
  21. package/dist/deviceProfile/buildDeviceProfile.d.ts +3 -4
  22. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
  23. package/dist/deviceProfile/index.d.ts +1 -1
  24. package/dist/deviceProfile/index.d.ts.map +1 -1
  25. package/dist/index.d.ts +571 -496
  26. package/dist/index.js +502 -571
  27. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  28. package/dist/types/api/getDeviceInfo.d.ts +2 -2
  29. package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
  30. package/dist/types/device.d.ts +87 -8
  31. package/dist/types/device.d.ts.map +1 -1
  32. package/dist/utils/capabilitieUtils.d.ts.map +1 -1
  33. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  34. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  35. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  36. package/dist/utils/findDefectiveBatchDevice.d.ts +1 -1
  37. package/dist/utils/patch.d.ts +1 -1
  38. package/package.json +4 -4
  39. package/src/api/FirmwareUpdateV2.ts +9 -2
  40. package/src/api/FirmwareUpdateV3.ts +2 -1
  41. package/src/api/FirmwareUpdateV4.ts +24 -31
  42. package/src/api/GetDeviceInfo.ts +6 -13
  43. package/src/api/GetOnekeyFeatures.ts +3 -14
  44. package/src/api/GetPassphraseState.ts +2 -2
  45. package/src/api/firmware/bootloaderHelper.ts +3 -2
  46. package/src/api/firmware/getBinary.ts +1 -1
  47. package/src/api/firmware/releaseHelper.ts +3 -3
  48. package/src/api/firmware/uploadFirmware.ts +5 -2
  49. package/src/api/tron/TronSignMessage.ts +0 -1
  50. package/src/api/tron/TronSignTransaction.ts +0 -1
  51. package/src/core/index.ts +3 -7
  52. package/src/data-manager/DataManager.ts +4 -4
  53. package/src/device/Device.ts +74 -240
  54. package/src/device/DevicePool.ts +3 -3
  55. package/src/deviceProfile/buildDeviceFeatures.ts +367 -0
  56. package/src/deviceProfile/buildDeviceProfile.ts +102 -155
  57. package/src/deviceProfile/index.ts +4 -1
  58. package/src/protocols/protocol-v2/features.ts +6 -9
  59. package/src/types/api/getDeviceInfo.ts +2 -2
  60. package/src/types/device.ts +97 -34
  61. package/src/utils/capabilitieUtils.ts +1 -2
  62. package/src/utils/deviceFeaturesUtils.ts +11 -17
  63. package/src/utils/deviceInfoUtils.ts +11 -29
  64. package/src/utils/deviceVersionUtils.ts +7 -25
  65. package/src/utils/findDefectiveBatchDevice.ts +6 -6
  66. package/dist/deviceProfile/legacyFeaturesView.d.ts +0 -5
  67. package/dist/deviceProfile/legacyFeaturesView.d.ts.map +0 -1
  68. package/src/deviceProfile/legacyFeaturesView.ts +0 -123
@@ -40,7 +40,6 @@ import {
40
40
  type Features,
41
41
  type IDeviceModel,
42
42
  type IDeviceType,
43
- type IVersionArray,
44
43
  type IVersionRange,
45
44
  type SupportFeatureType,
46
45
  type UnavailableCapabilities,
@@ -54,11 +53,7 @@ import {
54
53
  PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
55
54
  requestProtocolV2DeviceInfo,
56
55
  } from '../protocols/protocol-v2/features';
57
- import {
58
- buildProfileFromProtocolV1,
59
- buildProfileFromProtocolV2,
60
- buildProtocolV2GetFeaturesPayload,
61
- } from '../deviceProfile';
56
+ import { buildProtocolV1FeaturesPayload, buildProtocolV2FeaturesPayload } from '../deviceProfile';
62
57
 
63
58
  import type { PROTO } from '../constants';
64
59
  import type {
@@ -74,7 +69,6 @@ import type {
74
69
  Success,
75
70
  } from '@onekeyfe/hd-transport';
76
71
  import type DeviceConnector from './DeviceConnector';
77
- import type { DeviceProfile } from '../types/api/getDeviceInfo';
78
72
 
79
73
  export type InitOptions = {
80
74
  initSession?: boolean;
@@ -96,11 +90,6 @@ const parseRunOptions = (options?: RunOptions): RunOptions => {
96
90
 
97
91
  const Log = getLogger(LoggerNames.Device);
98
92
 
99
- const profileVersionToArray = (version?: string | null): IVersionArray | null => {
100
- if (!version) return null;
101
- return version.split('.').map(part => Number(part) || 0) as IVersionArray;
102
- };
103
-
104
93
  export interface DeviceEvents {
105
94
  [DEVICE.PIN]: [Device, PROTO.PinMatrixRequestType | undefined, (err: any, pin: string) => void];
106
95
  [DEVICE.PASSPHRASE_ON_DEVICE]: [Device, ((response: any) => void)?];
@@ -198,32 +187,12 @@ export class Device extends EventEmitter {
198
187
  /**
199
188
  * 唯一设备状态缓存。
200
189
  *
201
- * V1 直接保存原生 Features;V2 保存由 DevGetDeviceInfo/profile 映射出的
202
- * legacy Features 兼容视图。profile 作为结构化视图按需由 features 派生,
203
- * 不再单独维护第二份缓存。
190
+ * V1 直接保存原生 Features;V2 保存由 DevGetDeviceInfo 映射出的
191
+ * Features 视图。Device 不再保存 profile,结构化 DeviceProfile 只作为
192
+ * getDeviceInfo() 的 API 返回值存在。
204
193
  */
205
194
  features: Features | undefined = undefined;
206
195
 
207
- private getFeaturesProfile(): DeviceProfile | undefined {
208
- if (!this.features) return undefined;
209
- const protocol = this.originalDescriptor.protocolType === 'V2' ? 'V2' : 'V1';
210
- return buildProfileFromProtocolV1({
211
- protocol,
212
- features: this.features,
213
- onekeyFeatures: this.features as Parameters<typeof buildProfileFromProtocolV1>[0]['onekeyFeatures'],
214
- sources: ['features'],
215
- scope: 'verify',
216
- });
217
- }
218
-
219
- get profile(): DeviceProfile | undefined {
220
- return this.getFeaturesProfile();
221
- }
222
-
223
- set profile(profile: DeviceProfile | undefined) {
224
- this.updateProfile(profile);
225
- }
226
-
227
196
  /**
228
197
  * 是否需要更新设备信息。
229
198
  *
@@ -314,8 +283,7 @@ export class Device extends EventEmitter {
314
283
  label: label || 'OneKey',
315
284
  mode: this.getMode(),
316
285
  features,
317
- profile: this.profile,
318
- sessionId: this.features?.session_id ?? null,
286
+ sessionId: this.features?.sessionId ?? null,
319
287
  firmwareVersion: this.getFirmwareVersion(),
320
288
  bleFirmwareVersion: this.getBLEFirmwareVersion(),
321
289
  unavailableCapabilities: this.unavailableCapabilities,
@@ -509,9 +477,9 @@ export class Device extends EventEmitter {
509
477
  * 唯一协议判别器。
510
478
  *
511
479
  * descriptor.protocolType 是协议探测后的结果;V2 features 由 DevGetDeviceInfo
512
- * 映射产生,profile 只是 features 的结构化视图。
480
+ * 映射产生。
513
481
  * 全 SDK 的协议分支都必须走这里,不要直接读 originalDescriptor.protocolType
514
- * profile.protocol。
482
+ * 或从 features 反推。
515
483
  */
516
484
  getProtocol(): 'V1' | 'V2' {
517
485
  return this.originalDescriptor.protocolType === 'V2' ? 'V2' : 'V1';
@@ -522,57 +490,44 @@ export class Device extends EventEmitter {
522
490
  }
523
491
 
524
492
  getCurrentDeviceType() {
525
- return this.profile?.deviceType ?? getDeviceType(this.features);
493
+ return getDeviceType(this.features);
526
494
  }
527
495
 
528
496
  getCurrentDeviceId() {
529
- if (this.profile) {
530
- return this.profile.deviceId || undefined;
531
- }
532
- return this.features?.device_id || undefined;
497
+ return this.features?.deviceId || undefined;
533
498
  }
534
499
 
535
500
  getCurrentSerialNo() {
536
- if (this.profile) {
537
- return this.profile.serialNo || '';
538
- }
539
501
  return this.features ? getDeviceUUID(this.features) : '';
540
502
  }
541
503
 
542
504
  getCurrentBleName() {
543
- // V2 不回退 legacy features,避免缓存残留的 V1 数据泄漏到 Pro2 视图
544
- if (this.isProtocolV2()) return this.profile?.bleName ?? null;
545
- return this.profile?.bleName ?? getDeviceBleName(this.features);
505
+ return getDeviceBleName(this.features);
546
506
  }
547
507
 
548
508
  getCurrentLabel() {
549
- if (this.isProtocolV2()) return this.profile?.label ?? null;
550
- return this.profile?.label ?? getDeviceLabel(this.features);
509
+ return getDeviceLabel(this.features);
551
510
  }
552
511
 
553
512
  getCurrentPassphraseProtection() {
554
- if (this.profile) {
555
- return this.profile.status.passphraseProtection;
556
- }
557
- return this.features?.passphrase_protection;
513
+ return this.features?.passphraseProtection;
558
514
  }
559
515
 
560
516
  getCurrentFirmwareType() {
561
- return this.profile?.firmwareType ?? getFirmwareType(this.features);
517
+ return getFirmwareType(this.features);
562
518
  }
563
519
 
564
520
  getCurrentFirmwareVersionString() {
565
- return this.profile?.versions.firmware ?? getDeviceFirmwareVersion(this.features)?.join('.');
521
+ return getDeviceFirmwareVersion(this.features)?.join('.');
566
522
  }
567
523
 
568
524
  getCurrentBLEFirmwareVersionString() {
569
- if (this.profile?.versions.ble) return this.profile.versions.ble;
570
525
  if (!this.features) return undefined;
571
526
  return getDeviceBLEFirmwareVersion(this.features).join('.');
572
527
  }
573
528
 
574
529
  getCurrentSafetyChecks() {
575
- return this.features?.safety_checks;
530
+ return this.features?.safetyChecks;
576
531
  }
577
532
 
578
533
  getCurrentMethodVersionRange(
@@ -663,17 +618,25 @@ export class Device extends EventEmitter {
663
618
  return deviceId;
664
619
  }
665
620
 
621
+ private getSessionCacheDeviceKey(_deviceId?: string) {
622
+ const deviceId = _deviceId || this.getCurrentDeviceId();
623
+ if (deviceId) return deviceId;
624
+ if (this.isProtocolV2()) {
625
+ return this.originalDescriptor.path || this.originalDescriptor.id;
626
+ }
627
+ return undefined;
628
+ }
629
+
666
630
  getInternalState(_deviceId?: string) {
667
631
  Log.debug('getInternalState session cache: ', deviceSessionCache);
668
632
  Log.debug(
669
633
  'getInternalState session param: ',
670
634
  `device_id: ${_deviceId}`,
671
- `features.device_id: ${this.features?.device_id}`,
672
- `profile.deviceId: ${this.profile?.deviceId}`,
635
+ `features.deviceId: ${this.features?.deviceId}`,
673
636
  `passphraseState: ${this.passphraseState}`
674
637
  );
675
638
 
676
- const deviceId = _deviceId || this.getCurrentDeviceId();
639
+ const deviceId = this.getSessionCacheDeviceKey(_deviceId);
677
640
  if (!deviceId) return undefined;
678
641
  // Security invariant: no passphraseState → no session lookup.
679
642
  // A previous fallback that scanned `${deviceId}@*` keys could silently
@@ -691,7 +654,7 @@ export class Device extends EventEmitter {
691
654
  updateInternalState(
692
655
  enablePassphrase: boolean,
693
656
  passphraseState: string | undefined,
694
- deviceId: string,
657
+ deviceId: string | undefined,
695
658
  sessionId: string | null = null,
696
659
  featuresSessionId: string | null = null
697
660
  ) {
@@ -704,17 +667,21 @@ export class Device extends EventEmitter {
704
667
  `featuresSessionId: ${featuresSessionId}`
705
668
  );
706
669
 
670
+ const cacheDeviceKey = this.getSessionCacheDeviceKey(deviceId);
671
+ if (!cacheDeviceKey) return;
672
+
707
673
  if (enablePassphrase) {
708
674
  // update the sessionId
709
675
  if (sessionId) {
710
- deviceSessionCache[this.generateStateKey(deviceId, passphraseState)] = sessionId;
676
+ deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] = sessionId;
711
677
  } else if (featuresSessionId) {
712
- deviceSessionCache[this.generateStateKey(deviceId, passphraseState)] = featuresSessionId;
678
+ deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] =
679
+ featuresSessionId;
713
680
  }
714
681
  }
715
682
 
716
683
  // delete the old sessionId
717
- const oldKey = `${deviceId}`;
684
+ const oldKey = `${cacheDeviceKey}`;
718
685
  if (deviceSessionCache[oldKey]) {
719
686
  delete deviceSessionCache[oldKey];
720
687
  }
@@ -727,14 +694,13 @@ export class Device extends EventEmitter {
727
694
  'setInternalState session param: ',
728
695
  `state: ${state}`,
729
696
  `initSession: ${initSession}`,
730
- `device_id: ${this.features?.device_id}`,
731
- `profile.deviceId: ${this.profile?.deviceId}`,
697
+ `deviceId: ${this.features?.deviceId}`,
732
698
  `passphraseState: ${this.passphraseState}`
733
699
  );
734
700
 
735
701
  if (!this.passphraseState && !initSession) return;
736
702
 
737
- const deviceId = this.getCurrentDeviceId();
703
+ const deviceId = this.getSessionCacheDeviceKey();
738
704
  if (!deviceId) return;
739
705
 
740
706
  const key = this.generateStateKey(deviceId, this.passphraseState);
@@ -748,7 +714,7 @@ export class Device extends EventEmitter {
748
714
  clearInternalState(_deviceId?: string) {
749
715
  Log.debug('clearInternalState param: ', _deviceId);
750
716
 
751
- const deviceId = _deviceId || this.getCurrentDeviceId();
717
+ const deviceId = this.getSessionCacheDeviceKey(_deviceId);
752
718
  if (!deviceId) return;
753
719
  const key = `${deviceId}`;
754
720
  delete deviceSessionCache[key];
@@ -763,11 +729,10 @@ export class Device extends EventEmitter {
763
729
  // Protocol V2 不支持传统 Initialize,直接使用协议专用初始化流程。
764
730
  if (this.isProtocolV2()) {
765
731
  this.passphraseState = options?.passphraseState;
766
- if (this.profile && !this.featuresNeedsReload && !options?.initSession) {
767
- // 不能直接信任缓存 profile:设备端 wipe / 完成初始化 / 改 label 后
768
- // profile 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE
769
- // 单帧请求开销很小),用 applyProfileUpdate 字段级合并,
770
- // 不会降级已有的 verify / SE versions 数据。
732
+ if (this.features && !this.featuresNeedsReload && !options?.initSession) {
733
+ // 不能直接信任缓存 features:设备端 wipe / 完成初始化 / 改 label 后
734
+ // features 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE),
735
+ // 用字段级合并保留已有版本和 SE 信息。
771
736
  await this._refreshProtocolV2Status();
772
737
  return;
773
738
  }
@@ -821,11 +786,11 @@ export class Device extends EventEmitter {
821
786
  /**
822
787
  * Device initialization over Protocol V2.
823
788
  *
824
- * Protocol V2 不走传统 Initialize/GetFeatures;DeviceProfile 作为标准模型,
825
- * 同时维护一份由 profile/deviceInfo 适配出的 legacy Features,兼容旧方法内部判断。
789
+ * Protocol V2 不走传统 Initialize/GetFeatures;直接用 DevGetDeviceInfo
790
+ * 生成唯一的 features 状态。
826
791
  */
827
792
  private async _initializeProtocolV2() {
828
- Log.debug('Initialize device via Protocol V2 profile adapter');
793
+ Log.debug('Initialize device via Protocol V2 features adapter');
829
794
 
830
795
  try {
831
796
  // 超时由 requestProtocolV2DeviceInfo 内部的 typedCall timeoutMs(默认 10s)负责,
@@ -836,16 +801,8 @@ export class Device extends EventEmitter {
836
801
  });
837
802
  // 默认请求不含 SE/hash 数据,scope 如实标注为 basic;
838
803
  // 完整数据由 getDeviceInfo(scope:'verify'|'full') 获取。
839
- const profile = this.applyProfileUpdate(
840
- buildProfileFromProtocolV2({
841
- deviceInfo,
842
- sources: ['deviceInfo'],
843
- scope: 'basic',
844
- fallbackSerialNo: this.originalDescriptor?.path,
845
- }),
846
- deviceInfo
847
- );
848
- Log.debug('Protocol V2 profile:', profile);
804
+ const features = this.updateProtocolV2Features(deviceInfo);
805
+ Log.debug('Protocol V2 features:', features);
849
806
  this.featuresNeedsReload = false;
850
807
  } catch (error) {
851
808
  Log.error('Protocol V2 initialization failed:', error);
@@ -857,8 +814,7 @@ export class Device extends EventEmitter {
857
814
  * Protocol V2 的轻量状态刷新(每次 run 前调用)。
858
815
  *
859
816
  * 请求 hw + bt + status(不含 fw/SE target):status 提供 init_states / label /
860
- * passphrase_protection 等会在设备端变化的字段;hw/bt 提供 serialNo / bleName
861
- * 避免 applyProfileUpdate 的顶层字段覆盖把已有身份字段清空。
817
+ * passphrase_protection 等会在设备端变化的字段;hw/bt 提供 serialNo / bleName
862
818
  * versions 为空时按字段级合并保留旧值,verify 数据不会被降级。
863
819
  */
864
820
  private async _refreshProtocolV2Status() {
@@ -867,16 +823,8 @@ export class Device extends EventEmitter {
867
823
  commands: this.commands,
868
824
  request: PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
869
825
  });
870
- const profile = this.applyProfileUpdate(
871
- buildProfileFromProtocolV2({
872
- deviceInfo,
873
- sources: ['deviceInfo'],
874
- scope: 'basic',
875
- fallbackSerialNo: this.originalDescriptor?.path,
876
- }),
877
- deviceInfo
878
- );
879
- Log.debug('Protocol V2 profile (status refresh):', profile);
826
+ const features = this.updateProtocolV2Features(deviceInfo);
827
+ Log.debug('Protocol V2 features (status refresh):', features);
880
828
  } catch (error) {
881
829
  Log.error('Protocol V2 status refresh failed:', error);
882
830
  throw error;
@@ -888,108 +836,43 @@ export class Device extends EventEmitter {
888
836
  const deviceInfo = await requestProtocolV2DeviceInfo({
889
837
  commands: this.commands,
890
838
  });
891
- const profile = this.applyProfileUpdate(
892
- buildProfileFromProtocolV2({
893
- deviceInfo,
894
- sources: ['deviceInfo'],
895
- scope: 'basic',
896
- fallbackSerialNo: this.originalDescriptor?.path,
897
- }),
898
- deviceInfo
899
- );
900
- return this.features ?? this.updateProtocolV2Features(profile, deviceInfo);
839
+ return this.updateProtocolV2Features(deviceInfo);
901
840
  }
902
841
 
903
842
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
904
843
  this._updateFeatures(message);
905
- return message;
844
+ return this.features;
906
845
  }
907
846
 
908
- _updateFeatures(feat: Features, initSession?: boolean) {
847
+ _updateFeatures(protoFeatures: PROTO.Features | Features, initSession?: boolean) {
848
+ let feat =
849
+ 'protocol' in protoFeatures
850
+ ? protoFeatures
851
+ : buildProtocolV1FeaturesPayload(protoFeatures, this.features);
852
+
909
853
  // GetFeatures doesn't return 'session_id'
910
- if (this.features && this.features.session_id && !feat.session_id) {
911
- feat.session_id = this.features.session_id;
854
+ if (this.features?.sessionId && !feat.sessionId) {
855
+ feat.sessionId = this.features.sessionId;
912
856
  }
913
- if (this.getCurrentDeviceId() && feat.session_id) {
914
- this.setInternalState(feat.session_id, initSession);
857
+ if (this.getCurrentDeviceId() && feat.sessionId) {
858
+ this.setInternalState(feat.sessionId, initSession);
915
859
  }
916
860
  feat.unlocked = feat.unlocked ?? true;
917
861
 
918
862
  feat = fixFeaturesFirmwareVersion(feat);
919
863
 
920
864
  this.features = feat;
921
- if (!this.isProtocolV2()) {
922
- this.updateProfile(
923
- buildProfileFromProtocolV1({
924
- features: feat,
925
- sources: ['features'],
926
- })
927
- );
928
- }
929
865
  this.featuresNeedsReload = false;
930
866
  this.emit(DEVICE.FEATURES, this, feat);
931
867
  }
932
868
 
933
- updateProfile(profile: DeviceProfile | undefined) {
934
- if (!profile) {
935
- this.features = undefined;
936
- return;
937
- }
938
- if (profile.protocol === 'V2') {
939
- this.updateProtocolV2Features(profile, profile.raw?.protocolV2DeviceInfo);
940
- return;
941
- }
942
- if (profile.raw?.features) {
943
- this.features = fixFeaturesFirmwareVersion(profile.raw.features);
944
- }
945
- }
946
-
947
- /**
948
- * 字段级合并刷新 profile,并返回合并后的结果。
949
- *
950
- * basic 范围的刷新(initialize / getFeatures)拿不到 SE 版本和 verify 数据,
951
- * 不能整体替换掉 getDeviceInfo(scope:'verify'|'full') 建立的完整 profile。
952
- */
953
- applyProfileUpdate(next: DeviceProfile, deviceInfo?: ProtocolV2DeviceInfo): DeviceProfile {
954
- const prev = this.profile;
955
- if (!prev || prev.protocol !== next.protocol) {
956
- this.updateProfile({
957
- ...next,
958
- raw: {
959
- ...next.raw,
960
- ...(deviceInfo ? { protocolV2DeviceInfo: deviceInfo } : {}),
961
- },
962
- });
963
- return next;
964
- }
965
-
966
- const versions = { ...prev.versions };
967
- for (const [key, value] of Object.entries(next.versions)) {
968
- if (value != null) {
969
- (versions as Record<string, string | null | undefined>)[key] = value;
970
- }
971
- }
972
-
973
- const merged: DeviceProfile = {
974
- ...prev,
975
- ...next,
976
- versions,
977
- verify: next.verify ?? prev.verify,
978
- raw: {
979
- ...prev.raw,
980
- ...next.raw,
981
- ...(deviceInfo ? { protocolV2DeviceInfo: deviceInfo } : {}),
982
- },
983
- };
984
- this.updateProfile(merged);
985
- return merged;
986
- }
987
-
988
- private updateProtocolV2Features(profile: DeviceProfile, deviceInfo?: ProtocolV2DeviceInfo) {
869
+ updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo) {
989
870
  const features = fixFeaturesFirmwareVersion(
990
- buildProtocolV2GetFeaturesPayload(profile, deviceInfo)
871
+ buildProtocolV2FeaturesPayload(deviceInfo, this.features)
991
872
  );
992
- this._updateFeatures(features);
873
+ this.features = features;
874
+ this.featuresNeedsReload = false;
875
+ this.emit(DEVICE.FEATURES, this, features);
993
876
  return features;
994
877
  }
995
878
 
@@ -1027,7 +910,6 @@ export class Device extends EventEmitter {
1027
910
  if (device.features) {
1028
911
  this._updateFeatures(device.features);
1029
912
  }
1030
- this.updateProfile(device.profile);
1031
913
  }
1032
914
 
1033
915
  async run(fn?: () => Promise<void>, options?: RunOptions) {
@@ -1160,17 +1042,7 @@ export class Device extends EventEmitter {
1160
1042
  }
1161
1043
 
1162
1044
  getMode() {
1163
- if (this.profile) {
1164
- if (this.profile.status.mode === 'bootloader') return EOneKeyDeviceMode.bootloader;
1165
- if (this.profile.status.mode === 'notInitialized') return EOneKeyDeviceMode.notInitialized;
1166
- if (this.profile.status.noBackup === true) return EOneKeyDeviceMode.backupMode;
1167
- if (this.profile.status.mode === 'normal') return EOneKeyDeviceMode.normal;
1168
- // mode 'unknown'(V2 设备未上报 init_states)保守按未初始化处理,
1169
- // 与 isInitialized() 的 fail-closed 行为保持一致。
1170
- if (this.isProtocolV2()) return EOneKeyDeviceMode.notInitialized;
1171
- }
1172
-
1173
- if (this.features?.bootloader_mode) {
1045
+ if (this.features?.bootloaderMode) {
1174
1046
  // bootloader mode
1175
1047
  return EOneKeyDeviceMode.bootloader;
1176
1048
  }
@@ -1180,7 +1052,7 @@ export class Device extends EventEmitter {
1180
1052
  return EOneKeyDeviceMode.notInitialized;
1181
1053
  }
1182
1054
 
1183
- if (this.features?.no_backup) {
1055
+ if (this.features?.noBackup) {
1184
1056
  // backup mode
1185
1057
  return EOneKeyDeviceMode.backupMode;
1186
1058
  }
@@ -1190,17 +1062,11 @@ export class Device extends EventEmitter {
1190
1062
  }
1191
1063
 
1192
1064
  getFirmwareVersion() {
1193
- const profileVersion = profileVersionToArray(this.profile?.versions.firmware);
1194
- if (profileVersion) return profileVersion;
1195
- if (this.isProtocolV2()) return null;
1196
1065
  if (!this.features) return null;
1197
1066
  return getDeviceFirmwareVersion(this.features);
1198
1067
  }
1199
1068
 
1200
1069
  getBLEFirmwareVersion() {
1201
- const profileVersion = profileVersionToArray(this.profile?.versions.ble);
1202
- if (profileVersion) return profileVersion;
1203
- if (this.isProtocolV2()) return null;
1204
1070
  if (!this.features) return null;
1205
1071
  return getDeviceBLEFirmwareVersion(this.features);
1206
1072
  }
@@ -1230,37 +1096,19 @@ export class Device extends EventEmitter {
1230
1096
  }
1231
1097
 
1232
1098
  isBootloader() {
1233
- if (this.profile) {
1234
- return (
1235
- this.profile.status.mode === 'bootloader' || this.profile.status.bootloaderMode === true
1236
- );
1237
- }
1238
- return this.features && !!this.features.bootloader_mode;
1099
+ return this.features && !!this.features.bootloaderMode;
1239
1100
  }
1240
1101
 
1241
1102
  isInitialized() {
1242
- if (this.profile) {
1243
- if (this.profile.status.initialized != null) return this.profile.status.initialized;
1244
- if (this.profile.status.mode === 'normal') return true;
1245
- if (this.profile.status.mode === 'notInitialized') return false;
1246
- // V2 设备未上报 init_states 时按未初始化处理(fail-closed):
1247
- // 未知状态放行会让未初始化设备绕过 NOT_INITIALIZE 门禁。
1248
- if (this.isProtocolV2()) return false;
1249
- if (this.features) return !!this.features.initialized;
1250
- return false;
1251
- }
1252
1103
  return this.features && !!this.features.initialized;
1253
1104
  }
1254
1105
 
1255
1106
  isSeedless() {
1256
- if (this.profile) {
1257
- return this.profile.status.noBackup === true;
1258
- }
1259
- return this.features && !!this.features.no_backup;
1107
+ return this.features && !!this.features.noBackup;
1260
1108
  }
1261
1109
 
1262
1110
  isUnacquired(): boolean {
1263
- return this.features === undefined && this.profile === undefined;
1111
+ return this.features === undefined;
1264
1112
  }
1265
1113
 
1266
1114
  hasUnexpectedMode(allow: string[], require: string[]) {
@@ -1291,7 +1139,7 @@ export class Device extends EventEmitter {
1291
1139
  deviceType === EDeviceType.Touch ||
1292
1140
  deviceType === EDeviceType.Pro ||
1293
1141
  deviceType === EDeviceType.Pro2;
1294
- const unlocked = this.profile ? this.profile.status.unlocked : this.features?.unlocked;
1142
+ const unlocked = this.features?.unlocked;
1295
1143
  const preCheckTouch = isModeT && unlocked === false;
1296
1144
  const passphraseProtection = this.getCurrentPassphraseProtection();
1297
1145
 
@@ -1319,7 +1167,6 @@ export class Device extends EventEmitter {
1319
1167
 
1320
1168
  async unlockDevice() {
1321
1169
  const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
1322
- // profile 优先的版本范围解析;features 仅作为 V1 capability 判断来源
1323
1170
  const versionRange = this.getCurrentMethodVersionRange(
1324
1171
  type => this.supportUnlockVersionRange()[type]
1325
1172
  );
@@ -1338,24 +1185,11 @@ export class Device extends EventEmitter {
1338
1185
 
1339
1186
  if (supportUnlock) {
1340
1187
  const res = await this.commands.typedCall('UnLockDevice', 'UnLockDeviceResponse');
1341
- // 解锁结果同步到 profile(标准模型),features 仅在 V1 缓存存在时回写
1342
- if (this.profile) {
1343
- this.updateProfile({
1344
- ...this.profile,
1345
- status: {
1346
- ...this.profile.status,
1347
- unlocked: res.message.unlocked == null ? null : res.message.unlocked,
1348
- ...(res.message.passphrase_protection != null
1349
- ? { passphraseProtection: res.message.passphrase_protection }
1350
- : {}),
1351
- },
1352
- });
1353
- }
1354
1188
  if (this.features) {
1355
1189
  this.features.unlocked = res.message.unlocked == null ? null : res.message.unlocked;
1356
- this.features.unlocked_attach_pin =
1190
+ this.features.unlockedAttachPin =
1357
1191
  res.message.unlocked_attach_pin == null ? undefined : res.message.unlocked_attach_pin;
1358
- this.features.passphrase_protection =
1192
+ this.features.passphraseProtection =
1359
1193
  res.message.passphrase_protection == null ? null : res.message.passphrase_protection;
1360
1194
 
1361
1195
  return Promise.resolve(this.features);
@@ -166,7 +166,7 @@ export class DevicePool extends EventEmitter {
166
166
  for (let i = this.connectedPool.length - 1; i >= 0; i--) {
167
167
  const descriptor = this.connectedPool[i];
168
168
  const device = await this._createDevice(descriptor, initOptions);
169
- Log.debug('emit DEVICE.CONNECT: ', device?.profile ?? device?.features);
169
+ Log.debug('emit DEVICE.CONNECT: ', device?.features);
170
170
  this.emitter.emit(DEVICE.CONNECT, device);
171
171
  this.connectedPool.splice(i, 1);
172
172
  }
@@ -203,7 +203,7 @@ export class DevicePool extends EventEmitter {
203
203
  this._addConnectedDeviceToPool(d);
204
204
  return;
205
205
  }
206
- Log.debug('emit DEVICE.CONNECT: ', device.profile ?? device.features);
206
+ Log.debug('emit DEVICE.CONNECT: ', device.features);
207
207
  this.emitter.emit(DEVICE.CONNECT, device);
208
208
  });
209
209
 
@@ -215,7 +215,7 @@ export class DevicePool extends EventEmitter {
215
215
  return;
216
216
  }
217
217
 
218
- Log.debug('emit DEVICE.DISCONNECT: ', device.profile ?? device.features);
218
+ Log.debug('emit DEVICE.DISCONNECT: ', device.features);
219
219
  this.emitter.emit(DEVICE.DISCONNECT, device);
220
220
  });
221
221
  }