@onekeyfe/hd-core 1.2.0-alpha.20 → 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 (226) 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-lifecycle-events.test.ts +1 -1
  5. package/__tests__/device-settings.test.ts +111 -6
  6. package/__tests__/device-state-events.test.ts +3 -3
  7. package/__tests__/device-state-mapper.test.ts +15 -3
  8. package/__tests__/device-state-store.test.ts +1 -1
  9. package/__tests__/device-wallet-session-store.test.ts +111 -7
  10. package/__tests__/deviceSettings.test.ts +55 -15
  11. package/__tests__/evmLedgerLegacySafety.test.ts +6 -5
  12. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  13. package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
  14. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  15. package/__tests__/firmware-update/firmware-update-plan.test.ts +326 -0
  16. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +200 -0
  17. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +39 -30
  18. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  19. package/__tests__/get-device-state.test.ts +7 -0
  20. package/__tests__/open-wallet-session.test.ts +253 -13
  21. package/__tests__/pro2Wallpaper.test.ts +7 -18
  22. package/__tests__/protocol-v2-unlock-policy.test.ts +45 -0
  23. package/__tests__/protocol-v2.test.ts +439 -282
  24. package/__tests__/public-device-state-api.test.ts +79 -68
  25. package/__tests__/public-pro2-api-boundary.test.ts +22 -29
  26. package/dist/api/BaseMethod.d.ts +2 -4
  27. package/dist/api/BaseMethod.d.ts.map +1 -1
  28. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  29. package/dist/api/ClearSessionCache.d.ts.map +1 -1
  30. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  31. package/dist/api/FirmwareUpdateV2.d.ts +11 -0
  32. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  33. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  34. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  35. package/dist/api/FirmwareUpdateV4.d.ts +29 -10
  36. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  37. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  38. package/dist/api/UploadPortfolio.d.ts +0 -1
  39. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  40. package/dist/api/conflux/ConfluxGetAddress.d.ts +1 -0
  41. package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
  42. package/dist/api/conflux/ConfluxSignMessage.d.ts +1 -0
  43. package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
  44. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +1 -0
  45. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
  46. package/dist/api/conflux/ConfluxSignTransaction.d.ts +1 -0
  47. package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
  48. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  49. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  50. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  51. package/dist/api/firmware/FirmwareArtifactSource.d.ts +26 -0
  52. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  53. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  54. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  55. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +7 -0
  56. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  57. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  58. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  59. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +24 -0
  60. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  61. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +25 -0
  62. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  63. package/dist/api/firmware/getBinary.d.ts +7 -0
  64. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  65. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  66. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  67. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  68. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  69. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  70. package/dist/api/index.d.ts +0 -20
  71. package/dist/api/index.d.ts.map +1 -1
  72. package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
  73. package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
  74. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  75. package/dist/core/RequestQueue.d.ts +1 -19
  76. package/dist/core/RequestQueue.d.ts.map +1 -1
  77. package/dist/core/index.d.ts +0 -1
  78. package/dist/core/index.d.ts.map +1 -1
  79. package/dist/data-manager/DataManager.d.ts +1 -0
  80. package/dist/data-manager/DataManager.d.ts.map +1 -1
  81. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  82. package/dist/device/Device.d.ts +1 -1
  83. package/dist/device/Device.d.ts.map +1 -1
  84. package/dist/device/DeviceCommands.d.ts.map +1 -1
  85. package/dist/device/DeviceFeaturesState.d.ts +0 -1
  86. package/dist/device/DeviceFeaturesState.d.ts.map +1 -1
  87. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  88. package/dist/device/DeviceStateProjector.d.ts.map +1 -1
  89. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  90. package/dist/events/call.d.ts +0 -7
  91. package/dist/events/call.d.ts.map +1 -1
  92. package/dist/events/core.d.ts +2 -2
  93. package/dist/events/core.d.ts.map +1 -1
  94. package/dist/events/iframe.d.ts +0 -1
  95. package/dist/events/iframe.d.ts.map +1 -1
  96. package/dist/events/logBlockEvent.d.ts.map +1 -1
  97. package/dist/index.d.ts +320 -277
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +3210 -1856
  100. package/dist/inject.d.ts +2 -3
  101. package/dist/inject.d.ts.map +1 -1
  102. package/dist/lowLevelInject.d.ts +1 -2
  103. package/dist/lowLevelInject.d.ts.map +1 -1
  104. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
  105. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +4 -0
  106. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
  107. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
  108. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  109. package/dist/topLevelInject.d.ts.map +1 -1
  110. package/dist/types/api/checkAllFirmwareRelease.d.ts +3 -0
  111. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  112. package/dist/types/api/deviceSettings.d.ts +2 -2
  113. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  114. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  115. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  116. package/dist/types/api/export.d.ts +4 -1
  117. package/dist/types/api/export.d.ts.map +1 -1
  118. package/dist/types/api/firmwareUpdate.d.ts +69 -0
  119. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  120. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  121. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  122. package/dist/types/api/firmwareUpdatePlan.d.ts +27 -0
  123. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  124. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  125. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  126. package/dist/types/api/index.d.ts +10 -23
  127. package/dist/types/api/index.d.ts.map +1 -1
  128. package/dist/types/api/openWalletSession.d.ts +1 -0
  129. package/dist/types/api/openWalletSession.d.ts.map +1 -1
  130. package/dist/types/api/protocolV2.d.ts +3 -84
  131. package/dist/types/api/protocolV2.d.ts.map +1 -1
  132. package/dist/types/api/sessionCache.d.ts +4 -1
  133. package/dist/types/api/sessionCache.d.ts.map +1 -1
  134. package/dist/types/device.d.ts +4 -4
  135. package/dist/types/device.d.ts.map +1 -1
  136. package/dist/types/settings.d.ts +13 -0
  137. package/dist/types/settings.d.ts.map +1 -1
  138. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  139. package/dist/utils/deviceSettings.d.ts +36 -7
  140. package/dist/utils/deviceSettings.d.ts.map +1 -1
  141. package/dist/utils/index.d.ts +2 -1
  142. package/dist/utils/index.d.ts.map +1 -1
  143. package/dist/utils/patch.d.ts +1 -1
  144. package/dist/utils/patch.d.ts.map +1 -1
  145. package/package.json +4 -4
  146. package/src/api/BaseMethod.ts +11 -12
  147. package/src/api/CheckAllFirmwareRelease.ts +16 -3
  148. package/src/api/ClearSessionCache.ts +4 -0
  149. package/src/api/FirmwareUpdate.ts +8 -1
  150. package/src/api/FirmwareUpdateV2.ts +284 -119
  151. package/src/api/FirmwareUpdateV3.ts +248 -55
  152. package/src/api/FirmwareUpdateV4.ts +816 -328
  153. package/src/api/OpenWalletSession.ts +74 -25
  154. package/src/api/UploadPortfolio.ts +3 -13
  155. package/src/api/conflux/ConfluxGetAddress.ts +2 -0
  156. package/src/api/conflux/ConfluxSignMessage.ts +2 -0
  157. package/src/api/conflux/ConfluxSignMessageCIP23.ts +2 -0
  158. package/src/api/conflux/ConfluxSignTransaction.ts +2 -0
  159. package/src/api/device/DeviceChangePin.ts +1 -1
  160. package/src/api/device/DeviceSettings.ts +66 -5
  161. package/src/api/device/DeviceUpdateBootloader.ts +114 -6
  162. package/src/api/device/DeviceWipe.ts +1 -1
  163. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  164. package/src/api/firmware/FirmwareHostBinding.ts +100 -0
  165. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +50 -0
  166. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  167. package/src/api/firmware/FirmwareUpdatePlan.ts +621 -0
  168. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +378 -0
  169. package/src/api/firmware/updateBootloader.ts +19 -4
  170. package/src/api/firmware/uploadFirmware.ts +140 -22
  171. package/src/api/helpers/protocolV2FileWrite.ts +6 -0
  172. package/src/api/index.ts +0 -21
  173. package/src/api/nexa/NexaSignTransaction.ts +2 -0
  174. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
  175. package/src/core/RequestQueue.ts +4 -123
  176. package/src/core/index.ts +25 -31
  177. package/src/data/messages/messages-protocol-v2.json +77 -4
  178. package/src/data-manager/DataManager.ts +33 -33
  179. package/src/data-manager/connectSettings.ts +11 -0
  180. package/src/device/Device.ts +51 -61
  181. package/src/device/DeviceCommands.ts +58 -14
  182. package/src/device/DeviceFeaturesState.ts +0 -9
  183. package/src/device/DeviceStateMapper.ts +26 -8
  184. package/src/device/DeviceStateProjector.ts +7 -1
  185. package/src/deviceProfile/buildDeviceFeatures.ts +2 -1
  186. package/src/events/call.ts +0 -6
  187. package/src/events/core.ts +0 -2
  188. package/src/events/iframe.ts +0 -1
  189. package/src/events/logBlockEvent.ts +1 -0
  190. package/src/index.ts +5 -4
  191. package/src/inject.ts +22 -35
  192. package/src/lowLevelInject.ts +5 -4
  193. package/src/protocols/protocol-v2/deviceSession.ts +1 -1
  194. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +2 -2
  195. package/src/protocols/protocol-v2/unlockPolicy.ts +105 -0
  196. package/src/protocols/protocol-v2/unlockRetry.ts +1 -3
  197. package/src/protocols/protocol-v2/walletSession.ts +68 -12
  198. package/src/topLevelInject.ts +5 -3
  199. package/src/types/api/checkAllFirmwareRelease.ts +3 -0
  200. package/src/types/api/deviceSettings.ts +17 -2
  201. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  202. package/src/types/api/export.ts +18 -0
  203. package/src/types/api/firmwareUpdate.ts +76 -0
  204. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  205. package/src/types/api/firmwareUpdatePlan.ts +36 -0
  206. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  207. package/src/types/api/index.ts +14 -41
  208. package/src/types/api/openWalletSession.ts +2 -0
  209. package/src/types/api/protocolV2.ts +3 -187
  210. package/src/types/api/sessionCache.ts +9 -4
  211. package/src/types/device.ts +5 -3
  212. package/src/types/settings.ts +13 -0
  213. package/src/utils/deviceFeaturesUtils.ts +5 -1
  214. package/src/utils/deviceSettings.ts +130 -56
  215. package/src/utils/index.ts +18 -1
  216. package/__tests__/RequestQueue.test.ts +0 -140
  217. package/__tests__/UploadPortfolio.test.ts +0 -46
  218. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts +0 -6
  219. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +0 -1
  220. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts +0 -16
  221. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +0 -1
  222. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts +0 -12
  223. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +0 -1
  224. package/src/api/protocol-v2/DeviceSettingsGet.ts +0 -18
  225. package/src/api/protocol-v2/DeviceSettingsPageShow.ts +0 -70
  226. package/src/api/protocol-v2/DeviceSettingsSet.ts +0 -44
@@ -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,27 +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 deviceSettingsGet } from './protocol-v2/DeviceSettingsGet';
55
- export { default as protocolInfoRequest } from './protocol-v2/ProtocolInfoRequest';
56
- export { default as ping } from './protocol-v2/Ping';
57
- export { default as deviceFirmwareUpdate } from './protocol-v2/DeviceFirmwareUpdate';
58
- export { default as deviceGetFirmwareUpdateStatus } from './protocol-v2/DeviceGetFirmwareUpdateStatus';
59
- export { default as deviceFactoryInfoSet } from './protocol-v2/DeviceFactoryInfoSet';
60
- export { default as deviceFactoryInfoGet } from './protocol-v2/DeviceFactoryInfoGet';
61
- export { default as deviceSettingsSet } from './protocol-v2/DeviceSettingsSet';
62
- export { default as deviceSettingsPageShow } from './protocol-v2/DeviceSettingsPageShow';
63
- export { default as filesystemPermissionFix } from './protocol-v2/FilesystemPermissionFix';
64
- export { default as filesystemFormat } from './protocol-v2/FilesystemFormat';
65
- export { default as fileRead } from './FileRead';
66
- export { default as fileWrite } from './FileWrite';
67
- export { default as fileDelete } from './FileDelete';
68
- export { default as dirList } from './DirList';
69
- export { default as dirMake } from './DirMake';
70
- export { default as dirRemove } from './DirRemove';
71
- export { default as pathInfo } from './PathInfo';
72
51
  export { default as uploadPortfolio } from './UploadPortfolio';
73
52
 
74
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
  }
@@ -1,47 +1,21 @@
1
- import { type Deferred, createDeferred } from '@onekeyfe/hd-shared';
2
-
3
1
  import { LoggerNames, getLogger } from '../utils';
4
2
 
3
+ import type { Deferred } from '@onekeyfe/hd-shared';
5
4
  import type { BaseMethod } from '../api/BaseMethod';
6
5
 
7
6
  const Log = getLogger(LoggerNames.Core);
8
- const OPERATION_PRE_CANCEL_TTL_MS = 30_000;
9
- const DEFAULT_BACKGROUND_PREEMPT_TIMEOUT_MS = 5_500;
10
-
11
7
  export type RequestTask = {
12
8
  id: number;
13
9
  method: BaseMethod;
14
10
  callPromise?: Deferred<any> | undefined;
15
11
  abortController?: AbortController;
16
- settled: Deferred<void>;
17
- released: Deferred<void>;
18
- };
19
-
20
- export type RequestAdmission =
21
- | { status: 'reserved'; task: RequestTask }
22
- | { status: 'busy'; blockingTask?: RequestTask };
23
-
24
- export type RequestAdmissionOptions = {
25
- backgroundPreemptTimeoutMs?: number;
26
12
  };
27
13
 
28
14
  export default class RequestQueue {
29
15
  private requestQueue = new Map<number, RequestTask>();
30
16
 
31
- private operationRequestIds = new Map<string, number>();
32
-
33
- private pendingOperationCancellations = new Map<string, number>();
34
-
35
17
  private pendingCallbackTasks = new Map<string, Deferred<void>>();
36
18
 
37
- private prunePendingOperationCancellations(now = Date.now()) {
38
- this.pendingOperationCancellations.forEach((expiresAt, operationId) => {
39
- if (expiresAt <= now) {
40
- this.pendingOperationCancellations.delete(operationId);
41
- }
42
- });
43
- }
44
-
45
19
  // 生成唯一请求ID
46
20
  public generateRequestId = (method?: BaseMethod) => {
47
21
  if (method && method.responseID != null) {
@@ -57,93 +31,11 @@ export default class RequestQueue {
57
31
  }
58
32
  const abortController = new AbortController();
59
33
  method.abortSignal = abortController.signal;
60
- const task = {
61
- id: requestId,
62
- method,
63
- abortController,
64
- settled: createDeferred<void>(),
65
- released: createDeferred<void>(),
66
- };
34
+ const task = { id: requestId, method, abortController };
67
35
  this.requestQueue.set(requestId, task);
68
- const operationId = method.payload?.operationId;
69
- if (typeof operationId === 'string' && operationId) {
70
- this.operationRequestIds.set(operationId, requestId);
71
- this.prunePendingOperationCancellations();
72
- if (this.pendingOperationCancellations.delete(operationId)) {
73
- abortController.abort();
74
- }
75
- }
76
36
  return task;
77
37
  }
78
38
 
79
- public async admitTask(
80
- method: BaseMethod,
81
- options: RequestAdmissionOptions = {}
82
- ): Promise<RequestAdmission> {
83
- const activeTasks = Array.from(this.requestQueue.values()).filter(
84
- task => task.method.connectId === method.connectId
85
- );
86
-
87
- if (method.executionPriority === 'background') {
88
- if (activeTasks.length) {
89
- Log.debug(
90
- `Reject background method ${method.name}: device is occupied by ${activeTasks[0].method.name}`
91
- );
92
- return { status: 'busy', blockingTask: activeTasks[0] };
93
- }
94
- return { status: 'reserved', task: this.createTask(method) };
95
- }
96
-
97
- const backgroundTasks = activeTasks.filter(
98
- task => task.method.executionPriority === 'background'
99
- );
100
- if (!backgroundTasks.length) {
101
- return { status: 'reserved', task: this.createTask(method) };
102
- }
103
-
104
- Log.debug(
105
- `Preempt ${backgroundTasks.length} background request(s) for user method ${method.name}`
106
- );
107
- backgroundTasks.forEach(task => task.abortController?.abort());
108
- const timeoutMs =
109
- options.backgroundPreemptTimeoutMs ??
110
- Math.max(
111
- DEFAULT_BACKGROUND_PREEMPT_TIMEOUT_MS,
112
- ...backgroundTasks.map(task => task.method.maxAbortLatencyMs ?? 0)
113
- );
114
- const released = await this.waitForTasksReleased(backgroundTasks, timeoutMs);
115
- if (!released) {
116
- Log.warn(`Background request cleanup timed out before user method ${method.name}`);
117
- return { status: 'busy', blockingTask: backgroundTasks[0] };
118
- }
119
-
120
- return { status: 'reserved', task: this.createTask(method) };
121
- }
122
-
123
- private async waitForTasksReleased(tasks: RequestTask[], timeoutMs: number) {
124
- let timer: ReturnType<typeof setTimeout> | undefined;
125
- const timeoutPromise = new Promise<false>(resolve => {
126
- timer = setTimeout(() => resolve(false), timeoutMs);
127
- });
128
- const releasedPromise = Promise.all(tasks.map(task => task.released.promise)).then(() => true);
129
- const released = await Promise.race([releasedPromise, timeoutPromise]);
130
- if (timer) {
131
- clearTimeout(timer);
132
- }
133
- return released;
134
- }
135
-
136
- public abortOperation(operationId: string) {
137
- const requestId = this.operationRequestIds.get(operationId);
138
- if (requestId !== undefined) {
139
- return this.abortRequest(requestId);
140
- }
141
- const now = Date.now();
142
- this.prunePendingOperationCancellations(now);
143
- this.pendingOperationCancellations.set(operationId, now + OPERATION_PRE_CANCEL_TTL_MS);
144
- return false;
145
- }
146
-
147
39
  public getTask(requestId: number): RequestTask | undefined {
148
40
  return this.requestQueue.get(requestId);
149
41
  }
@@ -186,7 +78,6 @@ export default class RequestQueue {
186
78
  count++;
187
79
  }
188
80
  });
189
- this.pendingOperationCancellations.clear();
190
81
  return count;
191
82
  }
192
83
 
@@ -200,8 +91,8 @@ export default class RequestQueue {
200
91
  const request = this.requestQueue.get(requestId);
201
92
  if (request) {
202
93
  request.callPromise?.resolve(response);
203
- request.settled.resolve();
204
94
  }
95
+ this.releaseTask(requestId);
205
96
  }
206
97
 
207
98
  // 拒绝请求
@@ -209,22 +100,12 @@ export default class RequestQueue {
209
100
  const request = this.requestQueue.get(requestId);
210
101
  if (request) {
211
102
  request.callPromise?.reject(error);
212
- request.settled.resolve();
213
103
  }
104
+ this.releaseTask(requestId);
214
105
  }
215
106
 
216
107
  // 删除请求
217
108
  public releaseTask(requestId: number) {
218
- const task = this.requestQueue.get(requestId);
219
- const operationId = task?.method.payload?.operationId;
220
- if (
221
- typeof operationId === 'string' &&
222
- this.operationRequestIds.get(operationId) === requestId
223
- ) {
224
- this.operationRequestIds.delete(operationId);
225
- }
226
- task?.settled.resolve();
227
- task?.released.resolve();
228
109
  this.requestQueue.delete(requestId);
229
110
  }
230
111
 
package/src/core/index.ts CHANGED
@@ -196,25 +196,34 @@ export const callAPI = async (context: CoreContext, message: CoreMessage) => {
196
196
  // );
197
197
  // }
198
198
 
199
+ const { requestQueue, methodSynchronize } = context;
200
+ const error = await methodSynchronize(() => {
201
+ for (const requestId of requestQueue.getRequestTasksId()) {
202
+ const task = requestQueue.getTask(requestId);
203
+ Log.debug(
204
+ 'pre request task: ',
205
+ `task?.id: ${task?.id},
206
+ task?.method.connectId: ${task?.method.connectId},
207
+ task?.method.deviceId: ${task?.method.deviceId},
208
+ task?.method.name: ${task?.method.name}`
209
+ );
210
+ // if (task) {
211
+ // return Promise.reject(ERRORS.TypedError(HardwareErrorCode.DeviceBusy));
212
+ // }
213
+ }
214
+ return null;
215
+ });
216
+
217
+ if (error) {
218
+ return createResponseMessage(method.responseID, false, { error });
219
+ }
220
+
199
221
  // only the pre-warm signal (PreInitialize) forks here; normal methods fall
200
222
  // through to onCallDevice below, so the pre-warm dedup/guards never touch them
201
223
  if (method.isPreWarmSignal) {
202
224
  return handlePreWarmSignal(context, message, method);
203
225
  }
204
226
 
205
- const { requestQueue, methodSynchronize } = context;
206
- const admission = await methodSynchronize(
207
- () => requestQueue.admitTask(method),
208
- method.connectId ?? method.deviceId ?? 'unknown-device'
209
- );
210
- if (admission.status === 'busy') {
211
- const error = ERRORS.TypedError(HardwareErrorCode.DeviceBusy);
212
- DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
213
- completeMethodRequestContext(method, error);
214
- method.dispose();
215
- return createResponseMessage(method.responseID, false, { error });
216
- }
217
-
218
227
  return onCallDevice(context, message, method);
219
228
  };
220
229
 
@@ -334,7 +343,7 @@ const onCallDevice = async (
334
343
 
335
344
  await waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
336
345
 
337
- const task = requestQueue.getTask(method.responseID) ?? requestQueue.createTask(method);
346
+ const task = requestQueue.createTask(method);
338
347
 
339
348
  // Pre-warm holds the device as a per-connectId callback task so a concurrent
340
349
  // real call waits (before ensureConnected) instead of racing its Initialize.
@@ -378,10 +387,6 @@ const onCallDevice = async (
378
387
  if (method.payload?.onlyConnectBleDevice) {
379
388
  preWarmCallbackTask?.resolve();
380
389
  Log.debug('Call API - only connect ble device: ', device?.mainId);
381
- DevicePool.emitter.removeListener(DEVICE.CONNECT, onDeviceConnectHandler);
382
- requestQueue.releaseTask(method.responseID);
383
- completeMethodRequestContext(method);
384
- method.dispose();
385
390
  return createResponseMessage(method.responseID, true, null);
386
391
  }
387
392
 
@@ -661,9 +666,8 @@ const onCallDevice = async (
661
666
  skipInitialize: canSkipInitialize(method, device),
662
667
  ...parseInitOptions(method),
663
668
  };
664
- const deviceRunPromise = device.run(inner, runOptions);
665
- task.callPromise = createDeferred<any>();
666
- deviceRunPromise.catch(error => task.callPromise?.reject(error));
669
+ const deviceRun = () => device.run(inner, runOptions);
670
+ task.callPromise = createDeferred<any>(deviceRun);
667
671
 
668
672
  try {
669
673
  return await task.callPromise.promise;
@@ -671,8 +675,6 @@ const onCallDevice = async (
671
675
  Log.debug('Device Run Error: ', e);
672
676
  completeMethodRequestContext(method, e);
673
677
  return createResponseMessage(method.responseID, false, { error: e });
674
- } finally {
675
- await deviceRunPromise.catch(() => undefined);
676
678
  }
677
679
  } catch (error) {
678
680
  messageResponse = createResponseMessage(method.responseID, false, { error });
@@ -1389,10 +1391,6 @@ export default class Core extends EventEmitter {
1389
1391
  Log.debug(`[Core] Created SDK instance: ${this.sdkInstanceId}`);
1390
1392
  }
1391
1393
 
1392
- cancelOperation(operationId: string) {
1393
- this.requestQueue.abortOperation(operationId);
1394
- }
1395
-
1396
1394
  private getCoreContext() {
1397
1395
  return {
1398
1396
  sdkInstanceId: this.sdkInstanceId,
@@ -1468,10 +1466,6 @@ export default class Core extends EventEmitter {
1468
1466
  cancel(this.getCoreContext(), message.payload.connectId);
1469
1467
  break;
1470
1468
  }
1471
- case IFRAME.CANCEL_OPERATION: {
1472
- this.cancelOperation(message.payload.operationId);
1473
- break;
1474
- }
1475
1469
  case IFRAME.CALLBACK: {
1476
1470
  Log.log('callback message: ', message);
1477
1471
  postMessage(message);
@@ -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