@onekeyfe/hd-core 1.2.0-alpha.21 → 1.2.0-alpha.22

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 (175) hide show
  1. package/__tests__/DeviceCommands.test.ts +87 -2
  2. package/__tests__/connectSettings.test.ts +47 -5
  3. package/__tests__/device-identity.test.ts +24 -2
  4. package/__tests__/device-settings.test.ts +1 -1
  5. package/__tests__/device-wallet-session-store.test.ts +111 -7
  6. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  7. package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
  8. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  9. package/__tests__/firmware-update/firmware-update-plan.test.ts +326 -0
  10. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +200 -0
  11. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +39 -30
  12. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  13. package/__tests__/open-wallet-session.test.ts +253 -13
  14. package/__tests__/pro2Wallpaper.test.ts +7 -18
  15. package/__tests__/protocol-v2-unlock-policy.test.ts +45 -0
  16. package/__tests__/protocol-v2.test.ts +415 -145
  17. package/__tests__/public-device-state-api.test.ts +69 -62
  18. package/__tests__/public-pro2-api-boundary.test.ts +8 -34
  19. package/dist/api/BaseMethod.d.ts +2 -1
  20. package/dist/api/BaseMethod.d.ts.map +1 -1
  21. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  22. package/dist/api/ClearSessionCache.d.ts.map +1 -1
  23. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  24. package/dist/api/FirmwareUpdateV2.d.ts +11 -0
  25. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  26. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  27. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  28. package/dist/api/FirmwareUpdateV4.d.ts +29 -10
  29. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  30. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  31. package/dist/api/conflux/ConfluxGetAddress.d.ts +1 -0
  32. package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
  33. package/dist/api/conflux/ConfluxSignMessage.d.ts +1 -0
  34. package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
  35. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +1 -0
  36. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
  37. package/dist/api/conflux/ConfluxSignTransaction.d.ts +1 -0
  38. package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
  39. package/dist/api/device/DeviceUnlock.d.ts +1 -1
  40. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  41. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  42. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  43. package/dist/api/firmware/FirmwareArtifactSource.d.ts +26 -0
  44. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  45. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  46. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  47. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +7 -0
  48. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  49. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  50. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  51. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +24 -0
  52. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  53. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +25 -0
  54. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  55. package/dist/api/firmware/getBinary.d.ts +7 -0
  56. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  57. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  58. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  59. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  60. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  61. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  62. package/dist/api/index.d.ts +0 -17
  63. package/dist/api/index.d.ts.map +1 -1
  64. package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
  65. package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
  66. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  67. package/dist/data-manager/DataManager.d.ts +1 -0
  68. package/dist/data-manager/DataManager.d.ts.map +1 -1
  69. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  70. package/dist/device/Device.d.ts.map +1 -1
  71. package/dist/device/DeviceCommands.d.ts.map +1 -1
  72. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  73. package/dist/device/DeviceStateProjector.d.ts.map +1 -1
  74. package/dist/events/logBlockEvent.d.ts.map +1 -1
  75. package/dist/index.d.ts +265 -226
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +3045 -1550
  78. package/dist/inject.d.ts.map +1 -1
  79. package/dist/lowLevelInject.d.ts.map +1 -1
  80. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
  81. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +4 -0
  82. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
  83. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
  84. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  85. package/dist/topLevelInject.d.ts.map +1 -1
  86. package/dist/types/api/checkAllFirmwareRelease.d.ts +3 -0
  87. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  88. package/dist/types/api/deviceUnlock.d.ts +2 -2
  89. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  90. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  91. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  92. package/dist/types/api/export.d.ts +4 -1
  93. package/dist/types/api/export.d.ts.map +1 -1
  94. package/dist/types/api/firmwareUpdate.d.ts +69 -0
  95. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  96. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  97. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  98. package/dist/types/api/firmwareUpdatePlan.d.ts +27 -0
  99. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  100. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  101. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  102. package/dist/types/api/index.d.ts +10 -19
  103. package/dist/types/api/index.d.ts.map +1 -1
  104. package/dist/types/api/openWalletSession.d.ts +1 -0
  105. package/dist/types/api/openWalletSession.d.ts.map +1 -1
  106. package/dist/types/api/protocolV2.d.ts +3 -76
  107. package/dist/types/api/protocolV2.d.ts.map +1 -1
  108. package/dist/types/api/sessionCache.d.ts +4 -1
  109. package/dist/types/api/sessionCache.d.ts.map +1 -1
  110. package/dist/types/settings.d.ts +13 -0
  111. package/dist/types/settings.d.ts.map +1 -1
  112. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  113. package/dist/utils/patch.d.ts +1 -1
  114. package/dist/utils/patch.d.ts.map +1 -1
  115. package/package.json +4 -4
  116. package/src/api/BaseMethod.ts +8 -3
  117. package/src/api/CheckAllFirmwareRelease.ts +16 -3
  118. package/src/api/ClearSessionCache.ts +4 -0
  119. package/src/api/FirmwareUpdate.ts +8 -1
  120. package/src/api/FirmwareUpdateV2.ts +284 -119
  121. package/src/api/FirmwareUpdateV3.ts +248 -55
  122. package/src/api/FirmwareUpdateV4.ts +816 -328
  123. package/src/api/OpenWalletSession.ts +74 -25
  124. package/src/api/UploadPortfolio.ts +1 -1
  125. package/src/api/conflux/ConfluxGetAddress.ts +2 -0
  126. package/src/api/conflux/ConfluxSignMessage.ts +2 -0
  127. package/src/api/conflux/ConfluxSignMessageCIP23.ts +2 -0
  128. package/src/api/conflux/ConfluxSignTransaction.ts +2 -0
  129. package/src/api/device/DeviceChangePin.ts +1 -1
  130. package/src/api/device/DeviceUnlock.ts +1 -2
  131. package/src/api/device/DeviceUpdateBootloader.ts +114 -6
  132. package/src/api/device/DeviceWipe.ts +1 -1
  133. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  134. package/src/api/firmware/FirmwareHostBinding.ts +100 -0
  135. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +50 -0
  136. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  137. package/src/api/firmware/FirmwareUpdatePlan.ts +621 -0
  138. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +378 -0
  139. package/src/api/firmware/updateBootloader.ts +19 -4
  140. package/src/api/firmware/uploadFirmware.ts +140 -22
  141. package/src/api/helpers/protocolV2FileWrite.ts +6 -0
  142. package/src/api/index.ts +0 -18
  143. package/src/api/nexa/NexaSignTransaction.ts +2 -0
  144. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
  145. package/src/data/messages/messages-protocol-v2.json +77 -4
  146. package/src/data-manager/DataManager.ts +33 -33
  147. package/src/data-manager/connectSettings.ts +11 -0
  148. package/src/device/Device.ts +50 -60
  149. package/src/device/DeviceCommands.ts +58 -14
  150. package/src/device/DeviceStateMapper.ts +18 -1
  151. package/src/device/DeviceStateProjector.ts +7 -1
  152. package/src/events/logBlockEvent.ts +1 -0
  153. package/src/index.ts +5 -0
  154. package/src/inject.ts +22 -25
  155. package/src/lowLevelInject.ts +5 -1
  156. package/src/protocols/protocol-v2/deviceSession.ts +1 -1
  157. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +2 -2
  158. package/src/protocols/protocol-v2/unlockPolicy.ts +105 -0
  159. package/src/protocols/protocol-v2/unlockRetry.ts +1 -3
  160. package/src/protocols/protocol-v2/walletSession.ts +68 -12
  161. package/src/topLevelInject.ts +5 -1
  162. package/src/types/api/checkAllFirmwareRelease.ts +3 -0
  163. package/src/types/api/deviceUnlock.ts +2 -5
  164. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  165. package/src/types/api/export.ts +18 -0
  166. package/src/types/api/firmwareUpdate.ts +76 -0
  167. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  168. package/src/types/api/firmwareUpdatePlan.ts +36 -0
  169. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  170. package/src/types/api/index.ts +14 -34
  171. package/src/types/api/openWalletSession.ts +2 -0
  172. package/src/types/api/protocolV2.ts +3 -156
  173. package/src/types/api/sessionCache.ts +9 -4
  174. package/src/types/settings.ts +13 -0
  175. package/src/utils/deviceFeaturesUtils.ts +5 -1
@@ -152,6 +152,12 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
152
152
  options.throwIfAborted?.();
153
153
  }
154
154
  }
155
+ if (!response) {
156
+ throw ERRORS.TypedError(
157
+ HardwareErrorCode.RuntimeError,
158
+ 'FilesystemFileWrite completed without a response'
159
+ );
160
+ }
155
161
  options.throwIfAborted?.();
156
162
  lastMessage = response.message;
157
163
  const rawProcessedByte = response.message?.processed_byte;
package/src/api/index.ts CHANGED
@@ -48,24 +48,6 @@ export { default as promptWebDeviceAccess } from './PromptWebDeviceAccess';
48
48
  export { default as deviceReboot } from './protocol-v2/DeviceReboot';
49
49
  export { default as deviceGetOnboardingStatus } from './protocol-v2/DeviceGetOnboardingStatus';
50
50
  export { default as deviceUploadWallpaper } from './protocol-v2/DeviceUploadWallpaper';
51
- // Protocol V2 development APIs. These remain public while Pro2 integration is in development.
52
- export { default as deviceInfoGet } from './protocol-v2/DeviceInfoGet';
53
- export { default as deviceStatusGet } from './protocol-v2/DeviceStatusGet';
54
- export { default as protocolInfoRequest } from './protocol-v2/ProtocolInfoRequest';
55
- export { default as ping } from './protocol-v2/Ping';
56
- export { default as deviceFirmwareUpdate } from './protocol-v2/DeviceFirmwareUpdate';
57
- export { default as deviceGetFirmwareUpdateStatus } from './protocol-v2/DeviceGetFirmwareUpdateStatus';
58
- export { default as deviceFactoryInfoSet } from './protocol-v2/DeviceFactoryInfoSet';
59
- export { default as deviceFactoryInfoGet } from './protocol-v2/DeviceFactoryInfoGet';
60
- export { default as filesystemPermissionFix } from './protocol-v2/FilesystemPermissionFix';
61
- export { default as filesystemFormat } from './protocol-v2/FilesystemFormat';
62
- export { default as fileRead } from './FileRead';
63
- export { default as fileWrite } from './FileWrite';
64
- export { default as fileDelete } from './FileDelete';
65
- export { default as dirList } from './DirList';
66
- export { default as dirMake } from './DirMake';
67
- export { default as dirRemove } from './DirRemove';
68
- export { default as pathInfo } from './PathInfo';
69
51
  export { default as uploadPortfolio } from './UploadPortfolio';
70
52
 
71
53
  export { default as cipherKeyValue } from './CipherKeyValue';
@@ -9,6 +9,8 @@ import type { TypedCall } from '@onekeyfe/hd-transport';
9
9
  import type { DeviceFirmwareRange, NexaSignTransactionParams, NexaSignature } from '../../types';
10
10
 
11
11
  export default class NexaSignTransaction extends BaseMethod<NexaSignTransactionParams> {
12
+ checkDeviceId = true;
13
+
12
14
  hasBundle = false;
13
15
 
14
16
  init() {
@@ -71,7 +71,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
71
71
  this.path = `${WALLPAPER_DIRECTORY}/${normalizeFileName(fileName, this.encoded.data)}`;
72
72
  this.params = { width, height, rgba: rgbaBytes, fileName, chunkSize };
73
73
  this.requireProtocolV2 = true;
74
- this.unlockPolicy = 'retry-on-locked';
74
+ this.unlockPolicy = 'unlock-before-run';
75
75
  this.skipForceUpdateCheck = true;
76
76
  this.useDevicePassphraseState = false;
77
77
  }
@@ -482,9 +482,9 @@
482
482
  "MessageType_DeviceStatus": 60603,
483
483
  "MessageType_DevGetOnboardingStatus": 60604,
484
484
  "MessageType_DevOnboardingStatus": 60605,
485
- "MessageType_DeviceSessionGet": 60606,
486
485
  "MessageType_DeviceSession": 60607,
487
486
  "MessageType_DeviceSessionAskPin": 60608,
487
+ "MessageType_DeviceSessionOpen": 60609,
488
488
  "MessageType_FilesystemPermissionFix": 60800,
489
489
  "MessageType_FilesystemPathInfo": 60801,
490
490
  "MessageType_FilesystemPathInfoQuery": 60802,
@@ -506,7 +506,8 @@
506
506
  [90, 92],
507
507
  [114, 122],
508
508
  [300, 304],
509
- [309, 312]
509
+ [309, 312],
510
+ [60606, 60606]
510
511
  ]
511
512
  },
512
513
  "AlephiumGetAddress": {
@@ -11744,7 +11745,15 @@
11744
11745
  }
11745
11746
  },
11746
11747
  "ProtocolInfoRequest": {
11747
- "fields": {}
11748
+ "fields": {
11749
+ "eventless_wallet_session": {
11750
+ "type": "bool",
11751
+ "id": 1,
11752
+ "options": {
11753
+ "default": false
11754
+ }
11755
+ }
11756
+ }
11748
11757
  },
11749
11758
  "ProtocolInfo": {
11750
11759
  "fields": {
@@ -12427,14 +12436,78 @@
12427
12436
  }
12428
12437
  }
12429
12438
  },
12430
- "DeviceSessionGet": {
12439
+ "DeviceSessionErrorCode": {
12440
+ "values": {
12441
+ "DeviceSessionError_None": 0,
12442
+ "DeviceSessionError_UserCancelled": 1,
12443
+ "DeviceSessionError_InvalidSession": 2,
12444
+ "DeviceSessionError_AttachPinUnavailable": 3,
12445
+ "DeviceSessionError_PassphraseDisabled": 4,
12446
+ "DeviceSessionError_Busy": 5
12447
+ }
12448
+ },
12449
+ "DeviceSessionResume": {
12431
12450
  "fields": {
12432
12451
  "session_id": {
12452
+ "rule": "required",
12433
12453
  "type": "bytes",
12434
12454
  "id": 1
12435
12455
  }
12436
12456
  }
12437
12457
  },
12458
+ "DeviceSessionHostPassphrase": {
12459
+ "fields": {
12460
+ "passphrase": {
12461
+ "rule": "required",
12462
+ "type": "string",
12463
+ "id": 1
12464
+ }
12465
+ }
12466
+ },
12467
+ "DeviceSessionPassphraseOnDevice": {
12468
+ "fields": {}
12469
+ },
12470
+ "DeviceSessionAttachPinOnDevice": {
12471
+ "fields": {}
12472
+ },
12473
+ "DeviceSessionSelect": {
12474
+ "oneofs": {
12475
+ "access": {
12476
+ "oneof": ["host_passphrase", "passphrase_on_device", "attach_pin_on_device"]
12477
+ }
12478
+ },
12479
+ "fields": {
12480
+ "host_passphrase": {
12481
+ "type": "DeviceSessionHostPassphrase",
12482
+ "id": 1
12483
+ },
12484
+ "passphrase_on_device": {
12485
+ "type": "DeviceSessionPassphraseOnDevice",
12486
+ "id": 2
12487
+ },
12488
+ "attach_pin_on_device": {
12489
+ "type": "DeviceSessionAttachPinOnDevice",
12490
+ "id": 3
12491
+ }
12492
+ }
12493
+ },
12494
+ "DeviceSessionOpen": {
12495
+ "oneofs": {
12496
+ "mode": {
12497
+ "oneof": ["resume", "select"]
12498
+ }
12499
+ },
12500
+ "fields": {
12501
+ "resume": {
12502
+ "type": "DeviceSessionResume",
12503
+ "id": 1
12504
+ },
12505
+ "select": {
12506
+ "type": "DeviceSessionSelect",
12507
+ "id": 2
12508
+ }
12509
+ }
12510
+ },
12438
12511
  "DeviceSession": {
12439
12512
  "fields": {
12440
12513
  "session_id": {
@@ -399,7 +399,17 @@ export default class DataManager {
399
399
 
400
400
  static async load(settings: ConnectSettings) {
401
401
  this.settings = settings;
402
- if (!settings.fetchConfig) {
402
+ const manifestMode =
403
+ settings.firmwareManifestMode ?? (settings.fetchConfig ? 'sdk-managed' : undefined);
404
+ if (settings.preloadedConfig) {
405
+ this.applyRemoteConfig(settings.preloadedConfig);
406
+ Log.log('[DataConfig] Preloaded firmware config loaded');
407
+ return;
408
+ }
409
+ if (manifestMode === 'external-only') {
410
+ throw new Error('External firmware config snapshot is required');
411
+ }
412
+ if (manifestMode !== 'sdk-managed') {
403
413
  return;
404
414
  }
405
415
 
@@ -409,60 +419,50 @@ export default class DataManager {
409
419
 
410
420
  const urlWithCache = `${url}?noCache=${getTimeStamp()}`;
411
421
  let data: RemoteConfigResponse | null = null;
412
- let fetchMethod: 'configFetcher' | 'axios' | 'none' = 'none';
413
-
414
- // 1. Try custom configFetcher first (client-side IP direct connection support)
415
422
  if (settings.configFetcher) {
416
- Log.debug('[DataConfig] Trying configFetcher (client-side fetcher)...');
423
+ Log.debug('[DataConfig] Trying client configFetcher...');
417
424
  try {
418
425
  data = await settings.configFetcher(urlWithCache);
419
- if (data) {
420
- fetchMethod = 'configFetcher';
421
- Log.log('[DataConfig] ConfigFetcher success');
422
- } else {
423
- Log.debug('[DataConfig] ConfigFetcher returned null, will fallback to axios');
424
- }
425
426
  } catch (e) {
426
- Log.warn('[DataConfig] ConfigFetcher error, will fallback to axios:', e);
427
+ Log.warn('[DataConfig] Client configFetcher failed:', e);
427
428
  }
428
429
  }
429
-
430
- // 2. Fallback to default axios request
431
430
  if (!data) {
432
- Log.debug('[DataConfig] Trying axios (SDK default fetcher)...');
431
+ Log.debug('[DataConfig] Trying SDK-managed config request...');
433
432
  try {
434
433
  const response = await axios.get<RemoteConfigResponse>(urlWithCache, {
435
434
  // because of iframe timeout is 10000
436
435
  timeout: 7000,
437
436
  });
438
437
  data = response.data;
439
- fetchMethod = 'axios';
440
- Log.log('[DataConfig] Axios fetch success');
438
+ Log.log('[DataConfig] SDK-managed config request succeeded');
441
439
  } catch (e) {
442
- Log.warn('[DataConfig] Axios fetch error:', e);
440
+ Log.warn('[DataConfig] SDK-managed config request failed:', e);
443
441
  }
444
442
  }
445
443
 
446
- // 3. Apply config if available
447
444
  if (data) {
448
- Log.log(`[DataConfig] Config loaded successfully via [${fetchMethod}]`);
449
- this.deviceMap = {
450
- [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
451
- [EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
452
- [EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
453
- [EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
454
- [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
455
- [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
456
- [EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
457
- };
458
- this.assets = {
459
- bridge: data.bridge,
460
- };
445
+ this.applyRemoteConfig(data);
461
446
  } else {
462
- Log.warn('[DataConfig] All fetch methods failed, using built-in default config');
447
+ Log.warn('[DataConfig] SDK-managed config request failed, using built-in default config');
463
448
  }
464
449
  }
465
450
 
451
+ private static applyRemoteConfig(data: RemoteConfigResponse) {
452
+ this.deviceMap = {
453
+ [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
454
+ [EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
455
+ [EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
456
+ [EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
457
+ [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
458
+ [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
459
+ [EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
460
+ };
461
+ this.assets = {
462
+ bridge: data.bridge,
463
+ };
464
+ }
465
+
466
466
  static updateEnv(newEnv: ConnectSettings['env']) {
467
467
  if (this.settings) {
468
468
  const prevEnv = this.settings.env;
@@ -115,10 +115,21 @@ export const parseConnectSettings = (input: Partial<ConnectSettings> = {}) => {
115
115
  settings.fetchConfig = input.fetchConfig;
116
116
  }
117
117
 
118
+ if (
119
+ input.firmwareManifestMode === 'sdk-managed' ||
120
+ input.firmwareManifestMode === 'external-only'
121
+ ) {
122
+ settings.firmwareManifestMode = input.firmwareManifestMode;
123
+ }
124
+
118
125
  if (input.configFetcher) {
119
126
  settings.configFetcher = input.configFetcher;
120
127
  }
121
128
 
129
+ if (input.preloadedConfig) {
130
+ settings.preloadedConfig = input.preloadedConfig;
131
+ }
132
+
122
133
  return settings;
123
134
  };
124
135
 
@@ -153,7 +153,8 @@ export interface Device {
153
153
  * This allows short-lived processes (e.g. CLI) to restore a previously
154
154
  * obtained session, avoiding the need to re-enter passphrase on every
155
155
  * invocation. The session must have been obtained from a prior
156
- * getPassphraseState() call on the same device.
156
+ * wallet-session call on the same device. This compatibility hook is intended
157
+ * for a trusted CLI process restoring its own OS-keychain entry.
157
158
  *
158
159
  * @param deviceId - The device's device_id (from features)
159
160
  * @param passphraseState - The passphrase state token
@@ -222,24 +223,7 @@ export class Device extends EventEmitter {
222
223
 
223
224
  get features(): Features | undefined {
224
225
  if (!this.state) return undefined;
225
- const features = projectFeatures(this.state);
226
- const sessionCacheDeviceKey =
227
- this.state.identity.deviceId ||
228
- (this.isProtocolV2()
229
- ? this.originalDescriptor.path || this.originalDescriptor.id
230
- : undefined);
231
- const sessionId =
232
- sessionCacheDeviceKey && this.passphraseState
233
- ? deviceWalletSessionStore.get(sessionCacheDeviceKey, this.passphraseState)
234
- : undefined;
235
- return sessionId
236
- ? {
237
- ...features,
238
- sessionId,
239
- session_id: sessionId,
240
- passphraseState: this.passphraseState,
241
- }
242
- : features;
226
+ return projectFeatures(this.state);
243
227
  }
244
228
 
245
229
  set features(features: Features | undefined) {
@@ -334,7 +318,7 @@ export class Device extends EventEmitter {
334
318
  instanceId: this.instanceId,
335
319
  createdAt: this.createdAt,
336
320
  deviceType,
337
- /** ID for current seeds, will clear after replace a new seed at device */
321
+ /** Wallet-lifecycle ID; changes after the device is wiped or reinitialized. */
338
322
  deviceId,
339
323
  path: this.originalDescriptor?.path,
340
324
  bleName,
@@ -691,16 +675,14 @@ export class Device extends EventEmitter {
691
675
  const deviceId = _deviceId || this.getCurrentDeviceId();
692
676
  if (deviceId) return deviceId;
693
677
  if (this.isProtocolV2()) {
694
- return this.originalDescriptor.path || this.originalDescriptor.id;
678
+ return this.originalDescriptor.path;
695
679
  }
696
680
  return undefined;
697
681
  }
698
682
 
699
683
  private reconcileSessionCacheDeviceIdentity(previousDeviceId?: string) {
700
684
  deviceWalletSessionStore.reconcileDeviceIdentity({
701
- temporaryKey: this.isProtocolV2()
702
- ? this.originalDescriptor.path || this.originalDescriptor.id
703
- : undefined,
685
+ temporaryKey: this.isProtocolV2() ? this.originalDescriptor.path : undefined,
704
686
  previousDeviceId,
705
687
  nextDeviceId: this.getCurrentDeviceId(),
706
688
  });
@@ -806,43 +788,55 @@ export class Device extends EventEmitter {
806
788
  return;
807
789
  }
808
790
 
809
- // Log.debug('initialize param:', options);
791
+ try {
792
+ const callInitialize = async (payload: Record<string, unknown>, initSession?: boolean) => {
793
+ const initStartAt = Date.now();
794
+ const { message } = await this.commands.typedCall('Initialize', 'Features', payload, {
795
+ // iOS BLE bound devices can need close to 20 seconds.
796
+ timeoutMs: 25 * 1000,
797
+ });
798
+ this.setLastInitializeDuration(Date.now() - initStartAt);
799
+ this._updateFeatures(message, initSession);
800
+ await TransportManager.reconfigure(this.features);
801
+ };
810
802
 
811
- this.passphraseState = options?.passphraseState;
803
+ const expectedDeviceId = options?.deviceId;
804
+ if (expectedDeviceId) {
805
+ // Establish the live physical identity before sending wallet-bound state.
806
+ this.passphraseState = undefined;
807
+ await callInitialize({ is_contains_attach: true });
808
+ if (!this.checkDeviceId(expectedDeviceId)) {
809
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
810
+ }
811
+ }
812
812
 
813
- if (options?.initSession) {
814
- this.clearInternalState(options?.deviceId);
815
- }
813
+ this.passphraseState = options?.passphraseState;
816
814
 
817
- const internalState = this.getInternalState(options?.deviceId);
818
- const payload: any = {};
819
- if (internalState) {
820
- payload.session_id = internalState;
821
- }
815
+ if (options?.initSession) {
816
+ this.clearInternalState(options?.deviceId);
817
+ }
822
818
 
823
- if (options?.deriveCardano) {
824
- payload.derive_cardano = true;
825
- }
826
- payload.passphrase_state = options?.passphraseState;
827
- payload.is_contains_attach = true;
819
+ const internalState = this.getInternalState(options?.deviceId);
820
+ const payload: Record<string, unknown> = {
821
+ passphrase_state: options?.passphraseState,
822
+ is_contains_attach: true,
823
+ };
824
+ if (internalState) {
825
+ payload.session_id = internalState;
826
+ }
827
+ if (options?.deriveCardano) {
828
+ payload.derive_cardano = true;
829
+ }
828
830
 
829
- const initStartAt = Date.now();
830
- try {
831
- // @ts-expect-error
832
- const { message } = await Promise.race([
833
- this.commands.typedCall('Initialize', 'Features', payload),
834
- new Promise((_, reject) => {
835
- setTimeout(() => {
836
- reject(ERRORS.TypedError(HardwareErrorCode.DeviceInitializeFailed));
837
- // iOS ble bound device timeout 20s
838
- }, 25 * 1000);
839
- }),
840
- ]);
841
-
842
- const initCostMs = Date.now() - initStartAt;
843
- this.setLastInitializeDuration(initCostMs);
844
- this._updateFeatures(message, options?.initSession);
845
- await TransportManager.reconfigure(this.features);
831
+ const requiresWalletInitialize =
832
+ !expectedDeviceId ||
833
+ Boolean(internalState) ||
834
+ Boolean(options?.passphraseState) ||
835
+ options?.deriveCardano === true ||
836
+ options?.initSession === true;
837
+ if (requiresWalletInitialize) {
838
+ await callInitialize(payload, options?.initSession);
839
+ }
846
840
  } catch (error) {
847
841
  Log.error('Initialization failed:', error);
848
842
  throw error;
@@ -972,10 +966,6 @@ export class Device extends EventEmitter {
972
966
  ? protoFeatures
973
967
  : buildProtocolV1FeaturesPayload(protoFeatures, this.features);
974
968
 
975
- // GetFeatures doesn't return 'session_id'
976
- if (this.features?.sessionId && !feat.sessionId) {
977
- feat.sessionId = this.features.sessionId;
978
- }
979
969
  feat.unlocked = feat.unlocked ?? true;
980
970
 
981
971
  feat = fixFeaturesFirmwareVersion(feat);
@@ -1,6 +1,7 @@
1
1
  import { ERRORS, HardwareError, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
2
  import {
3
3
  DeviceErrorCode,
4
+ DeviceSessionErrorCode,
4
5
  type FailureType,
5
6
  type Messages,
6
7
  type Transport,
@@ -52,7 +53,7 @@ const HIGH_VOLUME_DEBUG_CALLS = new Set([
52
53
  'FirmwareUpload',
53
54
  'ResourceAck',
54
55
  ]);
55
- const SENSITIVE_DEBUG_CALLS = new Set(['DeviceSessionGet']);
56
+ const SENSITIVE_DEBUG_CALLS = new Set(['DeviceSessionOpen']);
56
57
 
57
58
  function shouldReduceDebugForCall(type: string) {
58
59
  return HIGH_VOLUME_DEBUG_CALLS.has(type);
@@ -337,20 +338,27 @@ export class DeviceCommands {
337
338
  if (this.disposed) {
338
339
  return;
339
340
  }
340
- this.dispose(true);
341
- if (this.callPromise) {
342
- try {
343
- await Promise.all([
344
- new Promise((_resolve, reject) =>
345
- // eslint-disable-next-line no-promise-executor-return
346
- setTimeout(() => reject(new Error('cancel timeout')), 10 * 1000)
347
- ),
348
- await this.callPromise,
349
- ]);
350
- } catch {
351
- // device error
341
+ const activeCall = this.callPromise;
342
+ let timer: ReturnType<typeof setTimeout> | undefined;
343
+ const cancellation = (async () => {
344
+ await this.dispose(true);
345
+ await activeCall?.catch(() => undefined);
346
+ })();
347
+
348
+ try {
349
+ await Promise.race([
350
+ cancellation,
351
+ new Promise<void>(resolve => {
352
+ timer = setTimeout(resolve, 10 * 1000);
353
+ }),
354
+ ]);
355
+ } finally {
356
+ if (timer) clearTimeout(timer);
357
+ if (this.callPromise === activeCall) {
352
358
  this.callPromise = undefined;
353
359
  }
360
+ // The cancellation work may finish after the bounded wait.
361
+ cancellation.catch(() => undefined);
354
362
  }
355
363
  }
356
364
 
@@ -451,7 +459,7 @@ export class DeviceCommands {
451
459
  'PinMatrixAck',
452
460
  'PassphraseAck',
453
461
  'Cancel',
454
- 'DeviceSessionGet',
462
+ 'DeviceSessionOpen',
455
463
  'BixinPinInputOnDevice',
456
464
  'FilesystemFileRead',
457
465
  'FilesystemFileWrite',
@@ -599,6 +607,42 @@ export class DeviceCommands {
599
607
  const isLegacyProtocolV2LockedFailure =
600
608
  this.device.isProtocolV2() && /^device (?:is )?locked$/i.test(normalizedMessage);
601
609
  if (
610
+ callType === 'DeviceSessionOpen' &&
611
+ subcode === DeviceSessionErrorCode.DeviceSessionError_InvalidSession
612
+ ) {
613
+ error = ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid, message, {
614
+ failureCode: code,
615
+ subcode,
616
+ firmwareMessage: message,
617
+ });
618
+ } else if (
619
+ callType === 'DeviceSessionOpen' &&
620
+ subcode === DeviceSessionErrorCode.DeviceSessionError_UserCancelled
621
+ ) {
622
+ error = ERRORS.TypedError(HardwareErrorCode.ActionCancelled, message, {
623
+ failureCode: code,
624
+ subcode,
625
+ firmwareMessage: message,
626
+ });
627
+ } else if (
628
+ callType === 'DeviceSessionOpen' &&
629
+ subcode === DeviceSessionErrorCode.DeviceSessionError_AttachPinUnavailable
630
+ ) {
631
+ error = ERRORS.TypedError(HardwareErrorCode.DeviceCheckUnlockTypeError, message, {
632
+ failureCode: code,
633
+ subcode,
634
+ firmwareMessage: message,
635
+ });
636
+ } else if (
637
+ callType === 'DeviceSessionOpen' &&
638
+ subcode === DeviceSessionErrorCode.DeviceSessionError_PassphraseDisabled
639
+ ) {
640
+ error = ERRORS.TypedError(HardwareErrorCode.DeviceNotOpenedPassphrase, message, {
641
+ failureCode: code,
642
+ subcode,
643
+ firmwareMessage: message,
644
+ });
645
+ } else if (
602
646
  subcode === DeviceErrorCode.DeviceError_ActionCancelled ||
603
647
  isLegacyProtocolV2ActionCancelledFailure
604
648
  ) {
@@ -29,6 +29,23 @@ const definedEntries = <T extends Record<string, unknown>>(value: T): T =>
29
29
  Object.entries(value).filter(([, fieldValue]) => fieldValue !== undefined)
30
30
  ) as T;
31
31
 
32
+ const sanitizeRawFeatures = (features: Features): Features['raw'] => {
33
+ if (!features.raw) return undefined;
34
+
35
+ const raw = { ...features.raw };
36
+ if (raw.protocolV1Features) {
37
+ const { session_id: _sessionId, ...protocolV1Features } = raw.protocolV1Features;
38
+ const compatibilityFields = protocolV1Features as typeof protocolV1Features & {
39
+ sessionId?: string;
40
+ passphraseState?: string;
41
+ };
42
+ delete compatibilityFields.sessionId;
43
+ delete compatibilityFields.passphraseState;
44
+ raw.protocolV1Features = compatibilityFields as PROTO.Features;
45
+ }
46
+ return raw;
47
+ };
48
+
32
49
  const imageVersion = (image?: DeviceFirmwareImageInfo | null) => image?.version ?? undefined;
33
50
 
34
51
  const bytesToHex = (value: unknown): string | undefined => {
@@ -127,7 +144,7 @@ export const mapFeaturesToState = (features: Features): DeviceStatePatch => ({
127
144
  },
128
145
  capabilities: features.capabilities,
129
146
  verification: definedEntries(features.verify ?? {}),
130
- raw: features.raw,
147
+ raw: sanitizeRawFeatures(features),
131
148
  });
132
149
 
133
150
  export const mapProtocolV1FeaturesToState = (
@@ -16,6 +16,12 @@ export const projectFeatures = (state: StoredDeviceState): Features => {
16
16
  delete publicRawFeatures.session_id;
17
17
  delete publicRawFeatures.sessionId;
18
18
  delete publicRawFeatures.passphraseState;
19
+ const publicRaw = snapshot.raw
20
+ ? {
21
+ ...snapshot.raw,
22
+ protocolV1Features: publicRawFeatures,
23
+ }
24
+ : undefined;
19
25
  const rawOneKeyFeatures = snapshot.raw?.protocolV1OneKeyFeatures ?? {};
20
26
  const bootloaderMode =
21
27
  snapshot.protocol === 'V1'
@@ -84,7 +90,7 @@ export const projectFeatures = (state: StoredDeviceState): Features => {
84
90
  se04BootVersion: snapshot.versions.se04Boot,
85
91
  seVersion: snapshot.versions.se ?? null,
86
92
  verify: snapshot.verification,
87
- raw: snapshot.raw,
93
+ raw: publicRaw,
88
94
  device_id: snapshot.identity.deviceId ?? undefined,
89
95
  ble_name: snapshot.identity.bleName ?? undefined,
90
96
  passphrase_protection: snapshot.status.passphraseProtection ?? undefined,
@@ -8,6 +8,7 @@ export const LogBlockEvent: Set<string> = new Set([
8
8
  const LogBlockMethod: Set<string> = new Set([
9
9
  'evmSignTypedData',
10
10
  'openWalletSession',
11
+ 'DeviceSessionOpen',
11
12
  'deviceUploadWallpaper',
12
13
  'uploadPortfolio',
13
14
  'fileWrite',
package/src/index.ts CHANGED
@@ -19,6 +19,11 @@ export * from './types';
19
19
  export { whitelist, whitelistExtension } from './data/config';
20
20
  export { executeCallback, cleanupCallback };
21
21
  export { preloadSessionCache } from './device/Device';
22
+ export {
23
+ getFirmwareUpdateHostBindingGeneration,
24
+ registerFirmwareUpdateHostBinding,
25
+ unregisterFirmwareUpdateHostBinding,
26
+ } from './api/firmware/FirmwareHostBinding';
22
27
 
23
28
  const HardwareSdk = ({
24
29
  init,