@onekeyfe/hd-core 1.2.0-alpha.75 → 1.2.0-alpha.76

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 (111) hide show
  1. package/__tests__/connectSettings.test.ts +45 -5
  2. package/__tests__/device-initialize-timeout.test.ts +56 -0
  3. package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
  4. package/__tests__/firmware-update/device-update-bootloader.test.ts +87 -0
  5. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  6. package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
  7. package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
  8. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +200 -0
  9. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  10. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +394 -0
  11. package/__tests__/firmware-update/firmware-update-plan.test.ts +593 -0
  12. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +231 -0
  13. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +59 -31
  14. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +64 -0
  15. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  16. package/__tests__/protocol-v2.test.ts +405 -100
  17. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  18. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  19. package/dist/api/FirmwareUpdateV2.d.ts +10 -0
  20. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  21. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  22. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  23. package/dist/api/FirmwareUpdateV4.d.ts +25 -5
  24. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  25. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  26. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  27. package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
  28. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  29. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  30. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  31. package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
  32. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
  33. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +8 -0
  34. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  35. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  36. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  37. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +25 -0
  38. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  39. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +32 -0
  40. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  41. package/dist/api/firmware/getBinary.d.ts +7 -0
  42. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  43. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  44. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  45. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  46. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  47. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  48. package/dist/data-manager/DataManager.d.ts +1 -0
  49. package/dist/data-manager/DataManager.d.ts.map +1 -1
  50. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  51. package/dist/device/Device.d.ts +1 -0
  52. package/dist/device/Device.d.ts.map +1 -1
  53. package/dist/index.d.ts +179 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +3079 -1062
  56. package/dist/inject.d.ts.map +1 -1
  57. package/dist/lowLevelInject.d.ts.map +1 -1
  58. package/dist/topLevelInject.d.ts.map +1 -1
  59. package/dist/types/api/checkAllFirmwareRelease.d.ts +4 -0
  60. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  61. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  62. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  63. package/dist/types/api/export.d.ts +4 -1
  64. package/dist/types/api/export.d.ts.map +1 -1
  65. package/dist/types/api/firmwareUpdate.d.ts +68 -0
  66. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  67. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  68. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  69. package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
  70. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  71. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  72. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  73. package/dist/types/api/index.d.ts +9 -1
  74. package/dist/types/api/index.d.ts.map +1 -1
  75. package/dist/types/settings.d.ts +13 -0
  76. package/dist/types/settings.d.ts.map +1 -1
  77. package/dist/utils/deviceFeaturesCompat.d.ts +1 -0
  78. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  79. package/package.json +4 -4
  80. package/src/api/CheckAllFirmwareRelease.ts +57 -6
  81. package/src/api/FirmwareUpdate.ts +13 -1
  82. package/src/api/FirmwareUpdateV2.ts +296 -121
  83. package/src/api/FirmwareUpdateV3.ts +263 -57
  84. package/src/api/FirmwareUpdateV4.ts +881 -349
  85. package/src/api/device/DeviceUpdateBootloader.ts +125 -6
  86. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  87. package/src/api/firmware/FirmwareHostBinding.ts +100 -0
  88. package/src/api/firmware/FirmwarePreparationError.ts +12 -0
  89. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +72 -1
  90. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  91. package/src/api/firmware/FirmwareUpdatePlan.ts +772 -0
  92. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +449 -0
  93. package/src/api/firmware/updateBootloader.ts +19 -4
  94. package/src/api/firmware/uploadFirmware.ts +140 -22
  95. package/src/data-manager/DataManager.ts +62 -54
  96. package/src/data-manager/connectSettings.ts +11 -0
  97. package/src/device/Device.ts +6 -1
  98. package/src/index.ts +5 -0
  99. package/src/inject.ts +22 -2
  100. package/src/lowLevelInject.ts +5 -1
  101. package/src/topLevelInject.ts +5 -1
  102. package/src/types/api/checkAllFirmwareRelease.ts +4 -0
  103. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  104. package/src/types/api/export.ts +18 -0
  105. package/src/types/api/firmwareUpdate.ts +75 -0
  106. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  107. package/src/types/api/firmwareUpdatePlan.ts +38 -0
  108. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  109. package/src/types/api/index.ts +14 -0
  110. package/src/types/settings.ts +13 -0
  111. package/src/utils/deviceFeaturesCompat.ts +6 -0
@@ -10,6 +10,7 @@ import { bytesToHex } from '../helpers/hexUtils';
10
10
  import { DataManager } from '../../data-manager';
11
11
  import { DevicePool } from '../../device/DevicePool';
12
12
  import { buildProtocolV1FeaturesPayload } from '../../deviceProfile';
13
+ import { openFirmwareByteSource, readFirmwareByteSourceFully } from './FirmwareArtifactSource';
13
14
 
14
15
  import type { KnownDevice } from '../../types';
15
16
  import type { TypedCall, TypedResponseMessage } from '../../device/DeviceCommands';
@@ -17,6 +18,7 @@ import type { PROTO } from '../../constants';
17
18
  import type { CoreMessage, IFirmwareUpdateProgressType } from '../../events';
18
19
  import type { Success } from '@onekeyfe/hd-transport';
19
20
  import type { Device } from '../../device/Device';
21
+ import type { FirmwareByteSource } from './FirmwareArtifactSource';
20
22
 
21
23
  const NEW_BOOT_UPRATE_FIRMWARE_VERSION = '2.4.5';
22
24
  const SESSION_ERROR = 'session not found';
@@ -88,17 +90,20 @@ export const waitBleInstall = async (updateType: string) => {
88
90
  }
89
91
  };
90
92
 
91
- export const uploadFirmware = async (
93
+ const toArrayBuffer = (payload: ArrayBuffer | Buffer): ArrayBuffer => {
94
+ if (payload instanceof ArrayBuffer) {
95
+ return payload;
96
+ }
97
+ return new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength).slice().buffer;
98
+ };
99
+
100
+ export const uploadFirmwareFromSource = async (
92
101
  updateType: 'firmware' | 'ble',
93
102
  typedCall: TypedCall,
94
103
  postMessage: (message: CoreMessage) => void,
95
104
  device: Device,
96
- {
97
- payload,
98
- rebootOnSuccess,
99
- }: PROTO.FirmwareUpload & {
100
- rebootOnSuccess?: boolean;
101
- },
105
+ source: FirmwareByteSource,
106
+ rebootOnSuccess?: boolean,
102
107
  isUpdateBootloader?: boolean
103
108
  ) => {
104
109
  const deviceType = device.getCurrentDeviceType();
@@ -119,20 +124,20 @@ export const uploadFirmware = async (
119
124
  if (supportUpgradeFileHeader) {
120
125
  // Extract and validate firmware header (first 1KB)
121
126
  const HEADER_SIZE = 1024;
122
- if (payload.byteLength < HEADER_SIZE) {
127
+ if (source.size < HEADER_SIZE) {
123
128
  throw ERRORS.TypedError(
124
129
  HardwareErrorCode.RuntimeError,
125
- `firmware payload too small: ${payload.byteLength} bytes, expected at least ${HEADER_SIZE} bytes`
130
+ `firmware payload too small: ${source.size} bytes, expected at least ${HEADER_SIZE} bytes`
126
131
  );
127
132
  }
128
133
 
129
134
  Log.debug('Uploading firmware header:', {
130
135
  size: HEADER_SIZE,
131
- totalSize: payload.byteLength,
136
+ totalSize: source.size,
132
137
  });
133
138
  postProgressTip(device, 'UploadingFirmwareHeader', postMessage);
134
139
 
135
- const header = new Uint8Array(payload.slice(0, HEADER_SIZE));
140
+ const header = new Uint8Array(await source.readAt(0, HEADER_SIZE));
136
141
 
137
142
  try {
138
143
  const headerRes = await typedCall('UpgradeFileHeader', 'Success', {
@@ -169,6 +174,9 @@ export const uploadFirmware = async (
169
174
  postProgressMessage(device, 0, 'installingFirmware', postMessage);
170
175
  let updateResponse: TypedResponseMessage<'Success'>;
171
176
  try {
177
+ // Classic/Mini bootloaders accept one protobuf payload and do not expose
178
+ // a request-range protocol. Other device families remain fully streamed.
179
+ const payload = await readFirmwareByteSourceFully(source);
172
180
  updateResponse = await typedCall('FirmwareUpload', 'Success', {
173
181
  payload,
174
182
  });
@@ -200,9 +208,7 @@ export const uploadFirmware = async (
200
208
  updateType,
201
209
  postMessage,
202
210
  device,
203
- {
204
- payload,
205
- },
211
+ source,
206
212
  rebootOnSuccess
207
213
  );
208
214
  return response.message;
@@ -211,7 +217,7 @@ export const uploadFirmware = async (
211
217
 
212
218
  postConfirmationMessage(device);
213
219
  postProgressTip(device, 'ConfirmOnDevice', postMessage);
214
- const length = payload.byteLength;
220
+ const length = source.size;
215
221
 
216
222
  let response = await typedCall('FirmwareErase', ['FirmwareRequest', 'Success'], { length });
217
223
  postProgressTip(device, 'FirmwareEraseSuccess', postMessage);
@@ -220,7 +226,7 @@ export const uploadFirmware = async (
220
226
  const start = response.message.offset!;
221
227
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
222
228
  const end = response.message.offset! + response.message.length!;
223
- const chunk = payload.slice(start, end);
229
+ const chunk = await source.readAt(start, end - start);
224
230
 
225
231
  if (start > 0) {
226
232
  postProgressMessage(
@@ -249,11 +255,45 @@ export const uploadFirmware = async (
249
255
  throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'uploadFirmware: unknown device model');
250
256
  };
251
257
 
258
+ export const uploadFirmware = async (
259
+ updateType: 'firmware' | 'ble',
260
+ typedCall: TypedCall,
261
+ postMessage: (message: CoreMessage) => void,
262
+ device: Device,
263
+ {
264
+ payload,
265
+ rebootOnSuccess,
266
+ }: PROTO.FirmwareUpload & {
267
+ rebootOnSuccess?: boolean;
268
+ },
269
+ isUpdateBootloader?: boolean
270
+ ) => {
271
+ const source = await openFirmwareByteSource({
272
+ binary: toArrayBuffer(payload),
273
+ });
274
+ if (!source) {
275
+ throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'firmware payload is empty');
276
+ }
277
+ try {
278
+ return await uploadFirmwareFromSource(
279
+ updateType,
280
+ typedCall,
281
+ postMessage,
282
+ device,
283
+ source,
284
+ rebootOnSuccess,
285
+ isUpdateBootloader
286
+ );
287
+ } finally {
288
+ await source.close();
289
+ }
290
+ };
291
+
252
292
  const newTouchUpdateProcess = async (
253
293
  updateType: 'firmware' | 'ble',
254
294
  postMessage: (message: CoreMessage) => void,
255
295
  device: Device,
256
- { payload }: PROTO.FirmwareUpload,
296
+ source: FirmwareByteSource,
257
297
  rebootOnSuccess = true
258
298
  ) => {
259
299
  let typedCall = device.getCommands().typedCall.bind(device.getCommands());
@@ -263,13 +303,13 @@ const newTouchUpdateProcess = async (
263
303
  const env = DataManager.getSettings('env');
264
304
  const perPackageSize = DataManager.isBleConnect(env) ? 16 : 128;
265
305
  const chunkSize = 1024 * perPackageSize;
266
- const totalChunks = Math.ceil(payload.byteLength / chunkSize);
306
+ const totalChunks = Math.ceil(source.size / chunkSize);
267
307
  let offset = 0;
268
308
  for (let i = 0; i < totalChunks; i++) {
269
309
  const chunkStart = i * chunkSize;
270
- const chunkEnd = Math.min(chunkStart + chunkSize, payload.byteLength);
310
+ const chunkEnd = Math.min(chunkStart + chunkSize, source.size);
271
311
  const chunkLength = chunkEnd - chunkStart;
272
- const chunk = payload.slice(chunkStart, chunkEnd);
312
+ const chunk = await source.readAt(chunkStart, chunkLength);
273
313
  const overwrite = i === 0;
274
314
  const progress = Math.round(((i + 1) / totalChunks) * 100);
275
315
  const writeRes = await emmcFileWriteWithRetry(
@@ -503,6 +543,67 @@ export const updateResource = async (
503
543
  return processResourceRequest(typedCall, res, data);
504
544
  };
505
545
 
546
+ export const updateResourceFromSource = async (
547
+ typedCall: TypedCall,
548
+ fileName: string,
549
+ source: FirmwareByteSource,
550
+ onConfirmAfter?: () => void
551
+ ) => {
552
+ const initialLength = Math.min(INIT_DATA_CHUNK_SIZE, source.size);
553
+ const initialChunk = new Uint8Array(await source.readAt(0, initialLength));
554
+ let response = await typedCall('ResourceUpdate', ['ResourceRequest', 'Success'], {
555
+ file_name: fileName,
556
+ data_length: source.size,
557
+ initial_data_chunk: bytesToHex(initialChunk),
558
+ hash: bytesToHex(blake2s(initialChunk)),
559
+ });
560
+ onConfirmAfter?.();
561
+
562
+ while (response.type !== 'Success') {
563
+ const { offset, data_length: dataLength } = response.message;
564
+ if (
565
+ !Number.isSafeInteger(offset) ||
566
+ offset === undefined ||
567
+ offset < 0 ||
568
+ !Number.isSafeInteger(dataLength) ||
569
+ dataLength === undefined ||
570
+ dataLength <= 0 ||
571
+ offset + dataLength > source.size
572
+ ) {
573
+ throw ERRORS.TypedError(
574
+ HardwareErrorCode.RuntimeError,
575
+ 'Device requested an invalid firmware resource range'
576
+ );
577
+ }
578
+ const chunk = new Uint8Array(await source.readAt(offset, dataLength));
579
+ response = await typedCall('ResourceAck', ['ResourceRequest', 'Success'], {
580
+ data_chunk: bytesToHex(chunk),
581
+ hash: bytesToHex(blake2s(chunk)),
582
+ });
583
+ }
584
+ return response.message;
585
+ };
586
+
587
+ export const updateResourcesFromSources = async (
588
+ typedCall: TypedCall,
589
+ postMessage: (message: CoreMessage) => void,
590
+ device: Device,
591
+ entries: ReadonlyArray<{ entryName: string; source: FirmwareByteSource }>
592
+ ) => {
593
+ postProgressTip(device, 'UpdateSysResource', postMessage);
594
+ for (const [index, entry] of entries.entries()) {
595
+ await updateResourceFromSource(typedCall, entry.entryName, entry.source);
596
+ postProgressMessage(
597
+ device,
598
+ Math.floor(((index + 1) / entries.length) * 100),
599
+ 'installingFirmware',
600
+ postMessage
601
+ );
602
+ }
603
+ postProgressTip(device, 'UpdateSysResourceSuccess', postMessage);
604
+ return true;
605
+ };
606
+
506
607
  export const updateResources = async (
507
608
  typedCall: TypedCall,
508
609
  postMessage: (message: CoreMessage) => void,
@@ -537,11 +638,28 @@ export const updateBootloader = async (
537
638
  typedCall: TypedCall,
538
639
  postMessage: (message: CoreMessage) => void,
539
640
  device: Device,
540
- source: ArrayBuffer
641
+ data: ArrayBuffer
642
+ ) => {
643
+ const source = await openFirmwareByteSource({ binary: data });
644
+ if (!source) {
645
+ throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'bootloader payload is empty');
646
+ }
647
+ try {
648
+ return await updateBootloaderFromSource(typedCall, postMessage, device, source);
649
+ } finally {
650
+ await source.close();
651
+ }
652
+ };
653
+
654
+ export const updateBootloaderFromSource = async (
655
+ typedCall: TypedCall,
656
+ postMessage: (message: CoreMessage) => void,
657
+ device: Device,
658
+ source: FirmwareByteSource
541
659
  ) => {
542
660
  postProgressTip(device, 'UpdateBootloader', postMessage);
543
661
  postProgressMessage(device, Math.floor(0), 'installingFirmware', postMessage);
544
- await updateResource(typedCall, 'bootloader.bin', source, () => {
662
+ await updateResourceFromSource(typedCall, 'bootloader.bin', source, () => {
545
663
  postProcessingMessage('resource', postMessage);
546
664
  });
547
665
  postProgressMessage(device, Math.floor(100), 'installingFirmware', postMessage);
@@ -408,7 +408,20 @@ export default class DataManager {
408
408
  static async load(settings: ConnectSettings): Promise<boolean> {
409
409
  this.settings = settings;
410
410
  this.protocolV2ResourcesConfigError = undefined;
411
- if (!settings.fetchConfig) {
411
+ const manifestMode =
412
+ settings.firmwareManifestMode ?? (settings.fetchConfig ? 'sdk-managed' : undefined);
413
+ if (settings.preloadedConfig) {
414
+ this.applyRemoteConfig(settings.preloadedConfig);
415
+ Log.log('[DataConfig] Preloaded firmware config loaded');
416
+ return true;
417
+ }
418
+ if (manifestMode === 'external-only') {
419
+ Log.warn(
420
+ '[DataConfig] External firmware config is unavailable; SDK-managed networking remains disabled'
421
+ );
422
+ return false;
423
+ }
424
+ if (manifestMode !== 'sdk-managed') {
412
425
  return false;
413
426
  }
414
427
 
@@ -419,26 +432,19 @@ export default class DataManager {
419
432
  const urlWithCache = `${url}?noCache=${getTimeStamp()}`;
420
433
  let data: RemoteConfigResponse | null = null;
421
434
  let fetchMethod: 'configFetcher' | 'axios' | 'none' = 'none';
422
-
423
- // 1. Try custom configFetcher first (client-side IP direct connection support)
424
435
  if (settings.configFetcher) {
425
- Log.debug('[DataConfig] Trying configFetcher (client-side fetcher)...');
436
+ Log.debug('[DataConfig] Trying client configFetcher...');
426
437
  try {
427
438
  data = await settings.configFetcher(urlWithCache);
428
439
  if (data) {
429
440
  fetchMethod = 'configFetcher';
430
- Log.log('[DataConfig] ConfigFetcher success');
431
- } else {
432
- Log.debug('[DataConfig] ConfigFetcher returned null, will fallback to axios');
433
441
  }
434
442
  } catch (e) {
435
- Log.warn('[DataConfig] ConfigFetcher error, will fallback to axios:', e);
443
+ Log.warn('[DataConfig] Client configFetcher failed:', e);
436
444
  }
437
445
  }
438
-
439
- // 2. Fallback to default axios request
440
446
  if (!data) {
441
- Log.debug('[DataConfig] Trying axios (SDK default fetcher)...');
447
+ Log.debug('[DataConfig] Trying SDK-managed config request...');
442
448
  try {
443
449
  const response = await axios.get<RemoteConfigResponse>(urlWithCache, {
444
450
  // because of iframe timeout is 10000
@@ -446,61 +452,63 @@ export default class DataManager {
446
452
  });
447
453
  data = response.data;
448
454
  fetchMethod = 'axios';
449
- Log.log('[DataConfig] Axios fetch success');
455
+ Log.log('[DataConfig] SDK-managed config request succeeded');
450
456
  } catch (e) {
451
- Log.warn('[DataConfig] Axios fetch error:', e);
457
+ Log.warn('[DataConfig] SDK-managed config request failed:', e);
452
458
  }
453
459
  }
454
460
 
455
- // 3. Apply config if available
456
461
  if (data) {
457
- let pro2Resources: IProtocolV2Resources | undefined;
458
- let neoResources: IProtocolV2Resources | undefined;
459
- this.protocolV2ResourcesConfigError = undefined;
460
- try {
461
- pro2Resources = parseProtocolV2Resources(
462
- (data.pro2 as { resources?: unknown } | undefined)?.resources
463
- );
464
- neoResources = parseProtocolV2Resources(
465
- (data.neo as { resources?: unknown } | undefined)?.resources
466
- );
467
- } catch (error) {
468
- // Firmware resource metadata is not required for base communication. If the
469
- // remote config is temporarily incomplete, disable this resource update only.
470
- this.protocolV2ResourcesConfigError =
471
- error instanceof Error ? error : new Error(String(error));
472
- Log.warn('[DataConfig] Ignoring invalid Pro2 resources config:', error);
473
- }
474
- const enrichedPro2Config = this.enrichFirmwareReleaseInfo(data.pro2);
475
- const enrichedNeoConfig = this.enrichFirmwareReleaseInfo(data.neo);
476
- const { resources: _unvalidatedResources, ...pro2Config } = enrichedPro2Config;
477
- const { resources: _unvalidatedNeoResources, ...neoConfig } = enrichedNeoConfig;
462
+ this.applyRemoteConfig(data);
478
463
  Log.log(`[DataConfig] Config loaded successfully via [${fetchMethod}]`);
479
- this.deviceMap = {
480
- [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
481
- [EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
482
- [EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
483
- [EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
484
- [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
485
- [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
486
- [EDeviceType.Pro2]: {
487
- ...pro2Config,
488
- ...(pro2Resources ? { resources: pro2Resources } : undefined),
489
- },
490
- [EDeviceType.Neo]: {
491
- ...neoConfig,
492
- ...(neoResources ? { resources: neoResources } : undefined),
493
- },
494
- };
495
- this.assets = {
496
- bridge: data.bridge,
497
- };
498
464
  return true;
499
465
  }
500
466
  Log.warn('[DataConfig] All fetch methods failed, using built-in default config');
501
467
  return false;
502
468
  }
503
469
 
470
+ private static applyRemoteConfig(data: RemoteConfigResponse) {
471
+ let pro2Resources: IProtocolV2Resources | undefined;
472
+ let neoResources: IProtocolV2Resources | undefined;
473
+ try {
474
+ pro2Resources = parseProtocolV2Resources(
475
+ (data.pro2 as { resources?: unknown } | undefined)?.resources
476
+ );
477
+ neoResources = parseProtocolV2Resources(
478
+ (data.neo as { resources?: unknown } | undefined)?.resources
479
+ );
480
+ } catch (error) {
481
+ // Firmware resource metadata is not required for base communication. If the
482
+ // remote config is temporarily incomplete, disable this resource update only.
483
+ this.protocolV2ResourcesConfigError =
484
+ error instanceof Error ? error : new Error(String(error));
485
+ Log.warn('[DataConfig] Ignoring invalid Protocol V2 resources config:', error);
486
+ }
487
+ const enrichedPro2Config = this.enrichFirmwareReleaseInfo(data.pro2);
488
+ const enrichedNeoConfig = this.enrichFirmwareReleaseInfo(data.neo);
489
+ const { resources: _unvalidatedResources, ...pro2Config } = enrichedPro2Config;
490
+ const { resources: _unvalidatedNeoResources, ...neoConfig } = enrichedNeoConfig;
491
+ this.deviceMap = {
492
+ [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
493
+ [EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
494
+ [EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
495
+ [EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
496
+ [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
497
+ [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
498
+ [EDeviceType.Pro2]: {
499
+ ...pro2Config,
500
+ ...(pro2Resources ? { resources: pro2Resources } : undefined),
501
+ },
502
+ [EDeviceType.Neo]: {
503
+ ...neoConfig,
504
+ ...(neoResources ? { resources: neoResources } : undefined),
505
+ },
506
+ };
507
+ this.assets = {
508
+ bridge: data.bridge,
509
+ };
510
+ }
511
+
504
512
  static updateEnv(newEnv: ConnectSettings['env']) {
505
513
  if (this.settings) {
506
514
  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
 
@@ -99,6 +99,11 @@ export type InitOptions = {
99
99
  protocolV2DeviceInfoTimeoutMs?: number;
100
100
  /** Refresh Protocol V2 runtime state before returning discovery results. */
101
101
  refreshRuntimeState?: boolean;
102
+ /**
103
+ * Protocol V1 Initialize response timeout override. Reboot-wait polling passes a
104
+ * short value so an unanswered probe settles before the next poll tick.
105
+ */
106
+ timeoutMs?: number;
102
107
  };
103
108
 
104
109
  export type RunOptions = {
@@ -909,7 +914,7 @@ export class Device extends EventEmitter {
909
914
  const initStartAt = Date.now();
910
915
  const { message } = await this.commands.typedCall('Initialize', 'Features', payload, {
911
916
  // iOS BLE bound devices can need close to 20 seconds.
912
- timeoutMs: 25 * 1000,
917
+ timeoutMs: options?.timeoutMs ?? 25 * 1000,
913
918
  });
914
919
  this.setLastInitializeDuration(Date.now() - initStartAt);
915
920
  this._updateFeatures(message, initSession);
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,
package/src/inject.ts CHANGED
@@ -1,7 +1,18 @@
1
+ import { getFirmwareUpdateCapabilities } from './api/firmware/FirmwareUpdateCapabilities';
2
+ import {
3
+ prepareFirmwareUpdatePlan,
4
+ validateFirmwareUpdatePreparedPlan,
5
+ } from './api/firmware/FirmwareUpdatePreparedPlan';
6
+ import {
7
+ getFirmwareUpdateHostBindingGeneration,
8
+ registerFirmwareUpdateHostBinding,
9
+ unregisterFirmwareUpdateHostBinding,
10
+ } from './api/firmware/FirmwareHostBinding';
11
+
1
12
  import type { HardwareConnectProtocol } from '@onekeyfe/hd-shared';
2
- import type { Unsuccessful } from './types';
3
13
  import type { EventEmitter } from 'events';
4
14
  import type { CallMethod } from './events';
15
+ import type { Unsuccessful } from './types';
5
16
  import type { CoreApi } from './types/api';
6
17
  import type { AllNetworkAddress } from './types/api/allNetworkGetAddress';
7
18
 
@@ -116,7 +127,10 @@ export const inject = ({
116
127
 
117
128
  setDeviceConnectProtocol: protocolAwareCall.setDeviceConnectProtocol,
118
129
 
119
- dispose,
130
+ dispose: async () => {
131
+ unregisterFirmwareUpdateHostBinding();
132
+ await dispose();
133
+ },
120
134
 
121
135
  uiResponse,
122
136
 
@@ -148,6 +162,12 @@ export const createCoreApi = (
148
162
  | 'updateSettings'
149
163
  | 'switchTransport'
150
164
  > => ({
165
+ getFirmwareUpdateCapabilities,
166
+ registerFirmwareUpdateHostBinding,
167
+ unregisterFirmwareUpdateHostBinding,
168
+ getFirmwareUpdateHostBindingGeneration,
169
+ prepareFirmwareUpdatePlan,
170
+ validateFirmwareUpdatePreparedPlan,
151
171
  getLogs: () => call({ method: 'getLogs' }),
152
172
  clearSessionCache: params => call({ ...params, method: 'clearSessionCache' }),
153
173
  /**
@@ -1,4 +1,5 @@
1
1
  import { createCoreApi, createProtocolAwareCall } from './inject';
2
+ import { unregisterFirmwareUpdateHostBinding } from './api/firmware/FirmwareHostBinding';
2
3
 
3
4
  import type { EventEmitter } from 'events';
4
5
  import type { CallMethod, CoreMessage } from './events';
@@ -46,7 +47,10 @@ export const lowLevelInject = ({
46
47
 
47
48
  setDeviceConnectProtocol: protocolAwareCall.setDeviceConnectProtocol,
48
49
 
49
- dispose,
50
+ dispose: async () => {
51
+ unregisterFirmwareUpdateHostBinding();
52
+ await dispose();
53
+ },
50
54
 
51
55
  uiResponse,
52
56
 
@@ -1,6 +1,7 @@
1
1
  import EventEmitter from 'events';
2
2
 
3
3
  import { createCoreApi, createProtocolAwareCall } from './inject';
4
+ import { unregisterFirmwareUpdateHostBinding } from './api/firmware/FirmwareHostBinding';
4
5
 
5
6
  import type { ConnectSettings } from './types/settings';
6
7
  import type { CoreApi } from './types/api';
@@ -49,7 +50,10 @@ export const topLevelInject = () => {
49
50
  lowLevelApi?.removeAllListeners(type);
50
51
  },
51
52
 
52
- dispose: () => lowLevelApi?.dispose(),
53
+ dispose: async () => {
54
+ unregisterFirmwareUpdateHostBinding();
55
+ await lowLevelApi?.dispose();
56
+ },
53
57
 
54
58
  uiResponse: response => lowLevelApi?.uiResponse(response),
55
59
 
@@ -12,6 +12,7 @@ import type {
12
12
  IProtocolV2FirmwareComponentTarget,
13
13
  } from '../settings';
14
14
  import type { FirmwareUpdateV4Target } from './firmwareUpdate';
15
+ import type { FirmwareUpdatePlan, FirmwareUpdatePlanForceTarget } from './firmwareUpdatePlan';
15
16
 
16
17
  export type FirmwareRelease = {
17
18
  shouldUpdate?: boolean;
@@ -64,12 +65,15 @@ export type AllFirmwareRelease = {
64
65
  components?: ProtocolV2FirmwareComponentRelease[];
65
66
  targetsToUpdate?: FirmwareUpdateV4Target[];
66
67
  release?: IFirmwareReleaseInfo;
68
+ firmwareUpdatePlan?: FirmwareUpdatePlan;
67
69
  };
68
70
 
69
71
  export type CheckAllFirmwareReleaseParams = {
70
72
  checkBridgeRelease?: boolean;
71
73
  checkFirmwareHash?: boolean;
72
74
  firmwareType?: EFirmwareType;
75
+ platform?: 'native' | 'desktop' | 'ext' | 'web' | 'web-embed';
76
+ forceUpdateTargets?: FirmwareUpdatePlanForceTarget[];
73
77
  };
74
78
 
75
79
  export declare function checkAllFirmwareRelease(
@@ -1,9 +1,15 @@
1
1
  import type { Success } from '@onekeyfe/hd-transport';
2
2
  import type { EFirmwareType } from '@onekeyfe/hd-shared';
3
+ import type { FirmwareArtifactReader, FirmwareArtifactReference } from './firmwareUpdate';
4
+ import type { FirmwareUpdatePreparedPlan } from './firmwareUpdatePreparedPlan';
3
5
  import type { Response } from '../params';
4
6
 
5
7
  export type DeviceUpdateBootloaderParams = {
8
+ preparedPlan?: FirmwareUpdatePreparedPlan;
9
+ hostBindingGeneration?: number;
6
10
  binary?: ArrayBuffer;
11
+ artifact?: FirmwareArtifactReference;
12
+ artifactReader?: FirmwareArtifactReader;
7
13
  firmwareType?: EFirmwareType;
8
14
  };
9
15
 
@@ -26,12 +26,30 @@ export type {
26
26
  DeviceUploadResourceResponse,
27
27
  } from './deviceUploadResource';
28
28
  export type {
29
+ FirmwareArtifactReader,
30
+ FirmwareArtifactReference,
31
+ FirmwareUpdateHostBinding,
32
+ FirmwareUpdateArtifactParams,
29
33
  FirmwareUpdateParams,
30
34
  FirmwareUpdateBinaryParams,
31
35
  FirmwareUpdateV3Params,
32
36
  FirmwareUpdateV4Params,
33
37
  FirmwareUpdateV4Target,
34
38
  } from './firmwareUpdate';
39
+ export type {
40
+ FirmwareUpdatePlan,
41
+ FirmwareUpdatePlanArtifact,
42
+ FirmwareUpdatePlanArtifactRole,
43
+ FirmwareUpdatePlanForceTarget,
44
+ FirmwareUpdatePlanTarget,
45
+ } from './firmwareUpdatePlan';
46
+ export type {
47
+ FirmwareUpdatePreparedArtifact,
48
+ FirmwareUpdatePreparedArtifactInput,
49
+ FirmwareUpdatePreparedEntry,
50
+ FirmwareUpdatePreparedPlan,
51
+ } from './firmwareUpdatePreparedPlan';
52
+ export type { FirmwareUpdateCapabilities } from './firmwareUpdateCapabilities';
35
53
  export type { AllFirmwareRelease } from './checkAllFirmwareRelease';
36
54
  export type {
37
55
  ProtocolV2FirmwareComponentRelease,