@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
@@ -114,11 +114,11 @@ export default class DataManager {
114
114
 
115
115
  const deviceFirmwareType = getFirmwareType(features);
116
116
  const deviceFirmwareVersion = getDeviceFirmwareVersion(features);
117
- if (features.firmware_present === false) {
117
+ if (features.firmwarePresent === false) {
118
118
  return 'none';
119
119
  }
120
120
 
121
- if (DeviceModelToTypes.model_mini.includes(deviceType) && features.bootloader_mode) {
121
+ if (DeviceModelToTypes.model_mini.includes(deviceType) && features.bootloaderMode) {
122
122
  return 'unknown';
123
123
  }
124
124
 
@@ -264,8 +264,8 @@ export default class DataManager {
264
264
  const targetDeviceConfigList = this.deviceMap[deviceType]?.[firmwareUpdateField] ?? [];
265
265
 
266
266
  if (
267
- features.firmware_present === false ||
268
- (DeviceModelToTypes.model_classic.includes(deviceType) && features.bootloader_mode)
267
+ features.firmwarePresent === false ||
268
+ (DeviceModelToTypes.model_classic.includes(deviceType) && features.bootloaderMode)
269
269
  ) {
270
270
  // Always return least changelog
271
271
  return getReleaseChangelog(targetDeviceConfigList, '0.0.0');
@@ -27,6 +27,8 @@ const initialSettings: ConnectSettings = {
27
27
  env: 'web',
28
28
  lazyLoad: false,
29
29
  timestamp: new Date().getTime(),
30
+ // 临时开关:仅用于本地/测试固件兼容;正式链路默认调用真实 DevGetDeviceInfo。
31
+ protocolV2DeviceInfoMockEnabled: false,
30
32
  };
31
33
 
32
34
  export const getEnv = () => {
@@ -115,6 +117,10 @@ export const parseConnectSettings = (input: Partial<ConnectSettings> = {}) => {
115
117
  settings.fetchConfig = input.fetchConfig;
116
118
  }
117
119
 
120
+ if (typeof input.protocolV2DeviceInfoMockEnabled === 'boolean') {
121
+ settings.protocolV2DeviceInfoMockEnabled = input.protocolV2DeviceInfoMockEnabled;
122
+ }
123
+
118
124
  return settings;
119
125
  };
120
126
 
@@ -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
  *
@@ -292,13 +261,14 @@ export class Device extends EventEmitter {
292
261
  const bleName = this.getCurrentBleName();
293
262
  const label = this.getCurrentLabel();
294
263
  const serialNo = this.getCurrentSerialNo();
264
+ const connectId = this.getConnectId();
295
265
  const deviceId = this.getCurrentDeviceId() || null;
296
266
 
297
267
  const features = this.features;
298
268
 
299
269
  return {
300
270
  /** Android uses Mac address, iOS uses uuid, USB uses uuid */
301
- connectId: DataManager.isBleConnect(env) ? this.mainId || null : serialNo,
271
+ connectId: DataManager.isBleConnect(env) ? this.mainId || null : connectId,
302
272
  /** Hardware ID, will not change at any time */
303
273
  uuid: serialNo,
304
274
  commType: this.originalDescriptor.commType,
@@ -314,8 +284,7 @@ export class Device extends EventEmitter {
314
284
  label: label || 'OneKey',
315
285
  mode: this.getMode(),
316
286
  features,
317
- profile: this.profile,
318
- sessionId: this.features?.session_id ?? null,
287
+ sessionId: this.features?.sessionId ?? null,
319
288
  firmwareVersion: this.getFirmwareVersion(),
320
289
  bleFirmwareVersion: this.getBLEFirmwareVersion(),
321
290
  unavailableCapabilities: this.unavailableCapabilities,
@@ -509,9 +478,9 @@ export class Device extends EventEmitter {
509
478
  * 唯一协议判别器。
510
479
  *
511
480
  * descriptor.protocolType 是协议探测后的结果;V2 features 由 DevGetDeviceInfo
512
- * 映射产生,profile 只是 features 的结构化视图。
481
+ * 映射产生。
513
482
  * 全 SDK 的协议分支都必须走这里,不要直接读 originalDescriptor.protocolType
514
- * profile.protocol。
483
+ * 或从 features 反推。
515
484
  */
516
485
  getProtocol(): 'V1' | 'V2' {
517
486
  return this.originalDescriptor.protocolType === 'V2' ? 'V2' : 'V1';
@@ -522,57 +491,54 @@ export class Device extends EventEmitter {
522
491
  }
523
492
 
524
493
  getCurrentDeviceType() {
525
- return this.profile?.deviceType ?? getDeviceType(this.features);
494
+ return getDeviceType(this.features);
526
495
  }
527
496
 
528
497
  getCurrentDeviceId() {
529
- if (this.profile) {
530
- return this.profile.deviceId || undefined;
531
- }
532
- return this.features?.device_id || undefined;
498
+ return this.features?.deviceId || undefined;
533
499
  }
534
500
 
535
501
  getCurrentSerialNo() {
536
- if (this.profile) {
537
- return this.profile.serialNo || '';
538
- }
539
502
  return this.features ? getDeviceUUID(this.features) : '';
540
503
  }
541
504
 
505
+ getConnectId() {
506
+ const serialNo = this.getCurrentSerialNo();
507
+ if (serialNo) return serialNo;
508
+
509
+ // connectId 是 SDK 内部连接路由 key;Protocol V2 早期固件/mock
510
+ // 可能还没有 serial_no,此时用 transport descriptor 兜底,不改变
511
+ // features.serialNo / deviceId 的业务语义。
512
+ return this.originalDescriptor.path || this.originalDescriptor.id || '';
513
+ }
514
+
542
515
  getCurrentBleName() {
543
- // V2 不回退 legacy features,避免缓存残留的 V1 数据泄漏到 Pro2 视图
544
- if (this.isProtocolV2()) return this.profile?.bleName ?? null;
545
- return this.profile?.bleName ?? getDeviceBleName(this.features);
516
+ return getDeviceBleName(this.features);
546
517
  }
547
518
 
548
519
  getCurrentLabel() {
549
- if (this.isProtocolV2()) return this.profile?.label ?? null;
550
- return this.profile?.label ?? getDeviceLabel(this.features);
520
+ return getDeviceLabel(this.features);
551
521
  }
552
522
 
553
523
  getCurrentPassphraseProtection() {
554
- if (this.profile) {
555
- return this.profile.status.passphraseProtection;
556
- }
557
- return this.features?.passphrase_protection;
524
+ return this.features?.passphraseProtection;
558
525
  }
559
526
 
560
527
  getCurrentFirmwareType() {
561
- return this.profile?.firmwareType ?? getFirmwareType(this.features);
528
+ return getFirmwareType(this.features);
562
529
  }
563
530
 
564
531
  getCurrentFirmwareVersionString() {
565
- return this.profile?.versions.firmware ?? getDeviceFirmwareVersion(this.features)?.join('.');
532
+ return getDeviceFirmwareVersion(this.features)?.join('.');
566
533
  }
567
534
 
568
535
  getCurrentBLEFirmwareVersionString() {
569
- if (this.profile?.versions.ble) return this.profile.versions.ble;
570
536
  if (!this.features) return undefined;
571
537
  return getDeviceBLEFirmwareVersion(this.features).join('.');
572
538
  }
573
539
 
574
540
  getCurrentSafetyChecks() {
575
- return this.features?.safety_checks;
541
+ return this.features?.safetyChecks;
576
542
  }
577
543
 
578
544
  getCurrentMethodVersionRange(
@@ -663,17 +629,25 @@ export class Device extends EventEmitter {
663
629
  return deviceId;
664
630
  }
665
631
 
632
+ private getSessionCacheDeviceKey(_deviceId?: string) {
633
+ const deviceId = _deviceId || this.getCurrentDeviceId();
634
+ if (deviceId) return deviceId;
635
+ if (this.isProtocolV2()) {
636
+ return this.originalDescriptor.path || this.originalDescriptor.id;
637
+ }
638
+ return undefined;
639
+ }
640
+
666
641
  getInternalState(_deviceId?: string) {
667
642
  Log.debug('getInternalState session cache: ', deviceSessionCache);
668
643
  Log.debug(
669
644
  'getInternalState session param: ',
670
645
  `device_id: ${_deviceId}`,
671
- `features.device_id: ${this.features?.device_id}`,
672
- `profile.deviceId: ${this.profile?.deviceId}`,
646
+ `features.deviceId: ${this.features?.deviceId}`,
673
647
  `passphraseState: ${this.passphraseState}`
674
648
  );
675
649
 
676
- const deviceId = _deviceId || this.getCurrentDeviceId();
650
+ const deviceId = this.getSessionCacheDeviceKey(_deviceId);
677
651
  if (!deviceId) return undefined;
678
652
  // Security invariant: no passphraseState → no session lookup.
679
653
  // A previous fallback that scanned `${deviceId}@*` keys could silently
@@ -691,7 +665,7 @@ export class Device extends EventEmitter {
691
665
  updateInternalState(
692
666
  enablePassphrase: boolean,
693
667
  passphraseState: string | undefined,
694
- deviceId: string,
668
+ deviceId: string | undefined,
695
669
  sessionId: string | null = null,
696
670
  featuresSessionId: string | null = null
697
671
  ) {
@@ -704,17 +678,21 @@ export class Device extends EventEmitter {
704
678
  `featuresSessionId: ${featuresSessionId}`
705
679
  );
706
680
 
681
+ const cacheDeviceKey = this.getSessionCacheDeviceKey(deviceId);
682
+ if (!cacheDeviceKey) return;
683
+
707
684
  if (enablePassphrase) {
708
685
  // update the sessionId
709
686
  if (sessionId) {
710
- deviceSessionCache[this.generateStateKey(deviceId, passphraseState)] = sessionId;
687
+ deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] = sessionId;
711
688
  } else if (featuresSessionId) {
712
- deviceSessionCache[this.generateStateKey(deviceId, passphraseState)] = featuresSessionId;
689
+ deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] =
690
+ featuresSessionId;
713
691
  }
714
692
  }
715
693
 
716
694
  // delete the old sessionId
717
- const oldKey = `${deviceId}`;
695
+ const oldKey = `${cacheDeviceKey}`;
718
696
  if (deviceSessionCache[oldKey]) {
719
697
  delete deviceSessionCache[oldKey];
720
698
  }
@@ -727,14 +705,13 @@ export class Device extends EventEmitter {
727
705
  'setInternalState session param: ',
728
706
  `state: ${state}`,
729
707
  `initSession: ${initSession}`,
730
- `device_id: ${this.features?.device_id}`,
731
- `profile.deviceId: ${this.profile?.deviceId}`,
708
+ `deviceId: ${this.features?.deviceId}`,
732
709
  `passphraseState: ${this.passphraseState}`
733
710
  );
734
711
 
735
712
  if (!this.passphraseState && !initSession) return;
736
713
 
737
- const deviceId = this.getCurrentDeviceId();
714
+ const deviceId = this.getSessionCacheDeviceKey();
738
715
  if (!deviceId) return;
739
716
 
740
717
  const key = this.generateStateKey(deviceId, this.passphraseState);
@@ -748,7 +725,7 @@ export class Device extends EventEmitter {
748
725
  clearInternalState(_deviceId?: string) {
749
726
  Log.debug('clearInternalState param: ', _deviceId);
750
727
 
751
- const deviceId = _deviceId || this.getCurrentDeviceId();
728
+ const deviceId = this.getSessionCacheDeviceKey(_deviceId);
752
729
  if (!deviceId) return;
753
730
  const key = `${deviceId}`;
754
731
  delete deviceSessionCache[key];
@@ -763,11 +740,10 @@ export class Device extends EventEmitter {
763
740
  // Protocol V2 不支持传统 Initialize,直接使用协议专用初始化流程。
764
741
  if (this.isProtocolV2()) {
765
742
  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 数据。
743
+ if (this.features && !this.featuresNeedsReload && !options?.initSession) {
744
+ // 不能直接信任缓存 features:设备端 wipe / 完成初始化 / 改 label 后
745
+ // features 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE),
746
+ // 用字段级合并保留已有版本和 SE 信息。
771
747
  await this._refreshProtocolV2Status();
772
748
  return;
773
749
  }
@@ -821,11 +797,11 @@ export class Device extends EventEmitter {
821
797
  /**
822
798
  * Device initialization over Protocol V2.
823
799
  *
824
- * Protocol V2 不走传统 Initialize/GetFeatures;DeviceProfile 作为标准模型,
825
- * 同时维护一份由 profile/deviceInfo 适配出的 legacy Features,兼容旧方法内部判断。
800
+ * Protocol V2 不走传统 Initialize/GetFeatures;直接用 DevGetDeviceInfo
801
+ * 生成唯一的 features 状态。
826
802
  */
827
803
  private async _initializeProtocolV2() {
828
- Log.debug('Initialize device via Protocol V2 profile adapter');
804
+ Log.debug('Initialize device via Protocol V2 features adapter');
829
805
 
830
806
  try {
831
807
  // 超时由 requestProtocolV2DeviceInfo 内部的 typedCall timeoutMs(默认 10s)负责,
@@ -836,16 +812,8 @@ export class Device extends EventEmitter {
836
812
  });
837
813
  // 默认请求不含 SE/hash 数据,scope 如实标注为 basic;
838
814
  // 完整数据由 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);
815
+ const features = this.updateProtocolV2Features(deviceInfo);
816
+ Log.debug('Protocol V2 features:', features);
849
817
  this.featuresNeedsReload = false;
850
818
  } catch (error) {
851
819
  Log.error('Protocol V2 initialization failed:', error);
@@ -857,8 +825,7 @@ export class Device extends EventEmitter {
857
825
  * Protocol V2 的轻量状态刷新(每次 run 前调用)。
858
826
  *
859
827
  * 请求 hw + bt + status(不含 fw/SE target):status 提供 init_states / label /
860
- * passphrase_protection 等会在设备端变化的字段;hw/bt 提供 serialNo / bleName
861
- * 避免 applyProfileUpdate 的顶层字段覆盖把已有身份字段清空。
828
+ * passphrase_protection 等会在设备端变化的字段;hw/bt 提供 serialNo / bleName
862
829
  * versions 为空时按字段级合并保留旧值,verify 数据不会被降级。
863
830
  */
864
831
  private async _refreshProtocolV2Status() {
@@ -867,16 +834,8 @@ export class Device extends EventEmitter {
867
834
  commands: this.commands,
868
835
  request: PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
869
836
  });
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);
837
+ const features = this.updateProtocolV2Features(deviceInfo);
838
+ Log.debug('Protocol V2 features (status refresh):', features);
880
839
  } catch (error) {
881
840
  Log.error('Protocol V2 status refresh failed:', error);
882
841
  throw error;
@@ -888,108 +847,43 @@ export class Device extends EventEmitter {
888
847
  const deviceInfo = await requestProtocolV2DeviceInfo({
889
848
  commands: this.commands,
890
849
  });
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);
850
+ return this.updateProtocolV2Features(deviceInfo);
901
851
  }
902
852
 
903
853
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
904
854
  this._updateFeatures(message);
905
- return message;
855
+ return this.features;
906
856
  }
907
857
 
908
- _updateFeatures(feat: Features, initSession?: boolean) {
858
+ _updateFeatures(protoFeatures: PROTO.Features | Features, initSession?: boolean) {
859
+ let feat =
860
+ 'protocol' in protoFeatures
861
+ ? protoFeatures
862
+ : buildProtocolV1FeaturesPayload(protoFeatures, this.features);
863
+
909
864
  // 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;
865
+ if (this.features?.sessionId && !feat.sessionId) {
866
+ feat.sessionId = this.features.sessionId;
912
867
  }
913
- if (this.getCurrentDeviceId() && feat.session_id) {
914
- this.setInternalState(feat.session_id, initSession);
868
+ if (this.getCurrentDeviceId() && feat.sessionId) {
869
+ this.setInternalState(feat.sessionId, initSession);
915
870
  }
916
871
  feat.unlocked = feat.unlocked ?? true;
917
872
 
918
873
  feat = fixFeaturesFirmwareVersion(feat);
919
874
 
920
875
  this.features = feat;
921
- if (!this.isProtocolV2()) {
922
- this.updateProfile(
923
- buildProfileFromProtocolV1({
924
- features: feat,
925
- sources: ['features'],
926
- })
927
- );
928
- }
929
876
  this.featuresNeedsReload = false;
930
877
  this.emit(DEVICE.FEATURES, this, feat);
931
878
  }
932
879
 
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) {
880
+ updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo) {
989
881
  const features = fixFeaturesFirmwareVersion(
990
- buildProtocolV2GetFeaturesPayload(profile, deviceInfo)
882
+ buildProtocolV2FeaturesPayload(deviceInfo, this.features)
991
883
  );
992
- this._updateFeatures(features);
884
+ this.features = features;
885
+ this.featuresNeedsReload = false;
886
+ this.emit(DEVICE.FEATURES, this, features);
993
887
  return features;
994
888
  }
995
889
 
@@ -1027,7 +921,6 @@ export class Device extends EventEmitter {
1027
921
  if (device.features) {
1028
922
  this._updateFeatures(device.features);
1029
923
  }
1030
- this.updateProfile(device.profile);
1031
924
  }
1032
925
 
1033
926
  async run(fn?: () => Promise<void>, options?: RunOptions) {
@@ -1160,17 +1053,7 @@ export class Device extends EventEmitter {
1160
1053
  }
1161
1054
 
1162
1055
  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) {
1056
+ if (this.features?.bootloaderMode) {
1174
1057
  // bootloader mode
1175
1058
  return EOneKeyDeviceMode.bootloader;
1176
1059
  }
@@ -1180,7 +1063,7 @@ export class Device extends EventEmitter {
1180
1063
  return EOneKeyDeviceMode.notInitialized;
1181
1064
  }
1182
1065
 
1183
- if (this.features?.no_backup) {
1066
+ if (this.features?.noBackup) {
1184
1067
  // backup mode
1185
1068
  return EOneKeyDeviceMode.backupMode;
1186
1069
  }
@@ -1190,17 +1073,11 @@ export class Device extends EventEmitter {
1190
1073
  }
1191
1074
 
1192
1075
  getFirmwareVersion() {
1193
- const profileVersion = profileVersionToArray(this.profile?.versions.firmware);
1194
- if (profileVersion) return profileVersion;
1195
- if (this.isProtocolV2()) return null;
1196
1076
  if (!this.features) return null;
1197
1077
  return getDeviceFirmwareVersion(this.features);
1198
1078
  }
1199
1079
 
1200
1080
  getBLEFirmwareVersion() {
1201
- const profileVersion = profileVersionToArray(this.profile?.versions.ble);
1202
- if (profileVersion) return profileVersion;
1203
- if (this.isProtocolV2()) return null;
1204
1081
  if (!this.features) return null;
1205
1082
  return getDeviceBLEFirmwareVersion(this.features);
1206
1083
  }
@@ -1230,37 +1107,19 @@ export class Device extends EventEmitter {
1230
1107
  }
1231
1108
 
1232
1109
  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;
1110
+ return this.features && !!this.features.bootloaderMode;
1239
1111
  }
1240
1112
 
1241
1113
  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
1114
  return this.features && !!this.features.initialized;
1253
1115
  }
1254
1116
 
1255
1117
  isSeedless() {
1256
- if (this.profile) {
1257
- return this.profile.status.noBackup === true;
1258
- }
1259
- return this.features && !!this.features.no_backup;
1118
+ return this.features && !!this.features.noBackup;
1260
1119
  }
1261
1120
 
1262
1121
  isUnacquired(): boolean {
1263
- return this.features === undefined && this.profile === undefined;
1122
+ return this.features === undefined;
1264
1123
  }
1265
1124
 
1266
1125
  hasUnexpectedMode(allow: string[], require: string[]) {
@@ -1291,7 +1150,7 @@ export class Device extends EventEmitter {
1291
1150
  deviceType === EDeviceType.Touch ||
1292
1151
  deviceType === EDeviceType.Pro ||
1293
1152
  deviceType === EDeviceType.Pro2;
1294
- const unlocked = this.profile ? this.profile.status.unlocked : this.features?.unlocked;
1153
+ const unlocked = this.features?.unlocked;
1295
1154
  const preCheckTouch = isModeT && unlocked === false;
1296
1155
  const passphraseProtection = this.getCurrentPassphraseProtection();
1297
1156
 
@@ -1319,7 +1178,6 @@ export class Device extends EventEmitter {
1319
1178
 
1320
1179
  async unlockDevice() {
1321
1180
  const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
1322
- // profile 优先的版本范围解析;features 仅作为 V1 capability 判断来源
1323
1181
  const versionRange = this.getCurrentMethodVersionRange(
1324
1182
  type => this.supportUnlockVersionRange()[type]
1325
1183
  );
@@ -1338,24 +1196,11 @@ export class Device extends EventEmitter {
1338
1196
 
1339
1197
  if (supportUnlock) {
1340
1198
  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
1199
  if (this.features) {
1355
1200
  this.features.unlocked = res.message.unlocked == null ? null : res.message.unlocked;
1356
- this.features.unlocked_attach_pin =
1201
+ this.features.unlockedAttachPin =
1357
1202
  res.message.unlocked_attach_pin == null ? undefined : res.message.unlocked_attach_pin;
1358
- this.features.passphrase_protection =
1203
+ this.features.passphraseProtection =
1359
1204
  res.message.passphrase_protection == null ? null : res.message.passphrase_protection;
1360
1205
 
1361
1206
  return Promise.resolve(this.features);