@onekeyfe/hd-core 1.2.0-alpha.86 → 1.2.0-alpha.88

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.
@@ -1,4 +1,5 @@
1
1
  import { EDeviceType, ERRORS, HardwareError, HardwareErrorCode, wait } from '@onekeyfe/hd-shared';
2
+ import JSZip from 'jszip';
2
3
  import {
3
4
  DeviceRebootType,
4
5
  PROTOCOL_V2_BLE_FILE_CHUNK_SIZE,
@@ -178,6 +179,10 @@ type ProtocolV2ResourceBundleSource = {
178
179
  headerHash?: string;
179
180
  };
180
181
 
182
+ type ProtocolV2ResourceBundleBinary = Omit<ProtocolV2ResourceBundleSource, 'source'> & {
183
+ binary: ArrayBuffer;
184
+ };
185
+
181
186
  type ProtocolV2ExecutionPhaseKind =
182
187
  | 'resource-sync'
183
188
  | 'bootloader-install'
@@ -270,6 +275,19 @@ const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map<number, FirmwareUpdateV4T
270
275
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_SE04, 'se04'],
271
276
  ]);
272
277
 
278
+ const PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET = new Map<
279
+ Exclude<FirmwareUpdateV4Target, 'resource'>,
280
+ ProtocolV2RemoteComponentTarget
281
+ >(
282
+ Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => [
283
+ PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID.get(target.targetId) as Exclude<
284
+ FirmwareUpdateV4Target,
285
+ 'resource'
286
+ >,
287
+ target,
288
+ ])
289
+ );
290
+
273
291
  const PROTOCOL_V2_ROMLOADER_UNSUPPORTED_MESSAGE =
274
292
  'FW_MGMT_TARGET_ROMLOADER is not accepted by the current Pro2 bootloader update request. Flash romloader with the loader-specific flow instead of firmwareUpdateV4.';
275
293
 
@@ -541,6 +559,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
541
559
  { name: 'se02Binary', type: 'buffer' },
542
560
  { name: 'se03Binary', type: 'buffer' },
543
561
  { name: 'se04Binary', type: 'buffer' },
562
+ { name: 'resourceArchiveBinary', type: 'buffer' },
544
563
  { name: 'firmwareType', type: 'string' },
545
564
  { name: 'targetsToUpdate', type: 'array', allowEmpty: true },
546
565
  { name: 'platform', type: 'string' },
@@ -558,37 +577,79 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
558
577
  const preparedPlan = payload.preparedPlan
559
578
  ? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
560
579
  : undefined;
580
+ const hasLocalArtifacts = [
581
+ payload.bootloaderBinary,
582
+ payload.romloaderBinary,
583
+ payload.applicationP1Binary,
584
+ payload.applicationP2Binary,
585
+ payload.coprocessorBinary,
586
+ payload.se01Binary,
587
+ payload.se02Binary,
588
+ payload.se03Binary,
589
+ payload.se04Binary,
590
+ payload.resourceArchiveBinary,
591
+ ].some(Boolean);
592
+ if (preparedPlan && hasLocalArtifacts) {
593
+ throw ERRORS.TypedError(
594
+ HardwareErrorCode.CallMethodInvalidParameter,
595
+ 'Prepared firmware plans cannot be combined with local firmware or resource binaries'
596
+ );
597
+ }
561
598
  const hostBinding =
562
- preparedPlan || payload.componentArtifacts
599
+ payload.hostBindingGeneration !== undefined
563
600
  ? resolveFirmwareUpdateHostBinding(
564
601
  payload.hostBindingGeneration,
565
602
  preparedPlan?.preparedPlanDigest
566
603
  )
567
604
  : undefined;
568
- if (hostBinding) {
569
- const componentArtifacts = (payload.componentArtifacts ?? {}) as NonNullable<
570
- FirmwareUpdateV4Params['componentArtifacts']
571
- >;
605
+ if (preparedPlan) {
572
606
  assertFirmwareUpdatePreparedPlanBinding({
573
- preparedPlan: preparedPlan ?? payload.preparedPlan,
607
+ preparedPlan,
574
608
  executor: 'v4',
575
609
  platform: payload.platform,
576
- scopeTargets: ['boot', 'app_v1', 'app_v2', 'coprocessor', 'se01', 'se02', 'se03', 'se04'],
577
- bindings: [
578
- ...Object.entries(componentArtifacts).flatMap(([target, artifact]) =>
579
- artifact
580
- ? [
581
- {
582
- target: target as Exclude<FirmwareUpdateV4Target, 'resource'>,
583
- artifact,
584
- },
585
- ]
586
- : []
587
- ),
588
- ],
610
+ scopeTargets: [],
611
+ bindings: [],
589
612
  });
613
+ if (payload.componentArtifacts) {
614
+ const componentBindings: Array<{
615
+ target: Exclude<FirmwareUpdateV4Target, 'resource'>;
616
+ artifact: FirmwareArtifactReference;
617
+ }> = Object.entries(payload.componentArtifacts).flatMap(([target, artifact]) =>
618
+ artifact
619
+ ? [
620
+ {
621
+ target: target as Exclude<FirmwareUpdateV4Target, 'resource'>,
622
+ artifact: artifact as FirmwareArtifactReference,
623
+ },
624
+ ]
625
+ : []
626
+ );
627
+ assertFirmwareUpdatePreparedPlanBinding({
628
+ preparedPlan,
629
+ executor: 'v4',
630
+ platform: payload.platform,
631
+ scopeTargets: componentBindings.map(binding => binding.target),
632
+ bindings: componentBindings,
633
+ });
634
+ }
590
635
  }
591
636
 
637
+ const preparedExpectedTargetVersions = preparedPlan?.artifacts.reduce<
638
+ NonNullable<FirmwareUpdateV4Params['expectedTargetVersions']>
639
+ >((result, artifact) => {
640
+ if (
641
+ artifact.role === 'component' &&
642
+ artifact.target !== 'resource' &&
643
+ artifact.targetVersion &&
644
+ PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.has(
645
+ artifact.target as Exclude<FirmwareUpdateV4Target, 'resource'>
646
+ )
647
+ ) {
648
+ result[artifact.target as FirmwareUpdateV4Target] = artifact.targetVersion;
649
+ }
650
+ return result;
651
+ }, {});
652
+
592
653
  this.params = {
593
654
  preparedPlan,
594
655
  chunkSize: payload.chunkSize,
@@ -602,13 +663,18 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
602
663
  se02Binary: payload.se02Binary,
603
664
  se03Binary: payload.se03Binary,
604
665
  se04Binary: payload.se04Binary,
605
- firmwareType: payload.firmwareType,
606
- targetsToUpdate: payload.targetsToUpdate,
607
- expectedTargetVersions: payload.expectedTargetVersions,
666
+ resourceArchiveBinary: payload.resourceArchiveBinary,
667
+ firmwareType: preparedPlan?.firmwareType ?? payload.firmwareType,
668
+ targetsToUpdate: preparedPlan
669
+ ? ([...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[])
670
+ : payload.targetsToUpdate,
671
+ expectedTargetVersions: preparedPlan
672
+ ? preparedExpectedTargetVersions
673
+ : payload.expectedTargetVersions,
608
674
  platform: payload.platform,
609
675
  expectedDeviceId: payload.expectedDeviceId,
610
676
  artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
611
- componentArtifacts: payload.componentArtifacts,
677
+ componentArtifacts: preparedPlan ? undefined : payload.componentArtifacts,
612
678
  };
613
679
  }
614
680
 
@@ -654,7 +720,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
654
720
  const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
655
721
  this.validateExpectedTargetVersions();
656
722
 
657
- if (this.params.preparedPlan) {
723
+ const hasPreparedComponentArtifacts = Object.values(this.params.componentArtifacts ?? {}).some(
724
+ Boolean
725
+ );
726
+ if (this.params.preparedPlan || hasPreparedComponentArtifacts) {
658
727
  return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
659
728
  }
660
729
  const wantsResources = !!this.params.targetsToUpdate?.includes('resource');
@@ -662,6 +731,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
662
731
  let fwBinaryMap: ProtocolV2TargetBinary[] = [];
663
732
  let bootloaderBinary: ArrayBuffer | null = null;
664
733
  let installItems: ProtocolV2InstallItem[] | undefined;
734
+ let resourceBundles: ProtocolV2ResourceBundleBinary[] = [];
665
735
  try {
666
736
  this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
667
737
  fwBinaryMap = this.collectExplicitTargetBinaries();
@@ -675,12 +745,17 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
675
745
  ? missingFirmwareTargets.length > 0
676
746
  : explicitInstallItems.length === 0;
677
747
  if (wantsResources) {
678
- throw ERRORS.TypedError(
679
- HardwareErrorCode.RuntimeError,
680
- 'Protocol V2 resource manifest must be prepared by the external firmware host',
681
- {
682
- firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
683
- }
748
+ if (!this.params.resourceArchiveBinary) {
749
+ throw ERRORS.TypedError(
750
+ HardwareErrorCode.RuntimeError,
751
+ 'Protocol V2 resource archive must be provided for a local resource update',
752
+ {
753
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
754
+ }
755
+ );
756
+ }
757
+ resourceBundles = await this.prepareProtocolV2LocalResourceArchive(
758
+ this.params.resourceArchiveBinary
684
759
  );
685
760
  }
686
761
  if (
@@ -726,7 +801,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
726
801
  throw normalizeFirmwarePreparationError(err);
727
802
  }
728
803
 
729
- if (!bootloaderBinary && fwBinaryMap.length === 0 && !installItems?.length) {
804
+ if (
805
+ !bootloaderBinary &&
806
+ fwBinaryMap.length === 0 &&
807
+ !installItems?.length &&
808
+ resourceBundles.length === 0
809
+ ) {
730
810
  throw ERRORS.TypedError(
731
811
  HardwareErrorCode.FirmwareUpdateDownloadFailed,
732
812
  'No firmware to update'
@@ -737,6 +817,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
737
817
  fwBinaryMap,
738
818
  bootloaderBinary,
739
819
  ...(installItems ? { installItems } : undefined),
820
+ ...(resourceBundles.length > 0 ? { resourceBundles } : undefined),
740
821
  });
741
822
  }
742
823
 
@@ -779,6 +860,54 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
779
860
  firmwareType: EFirmwareType,
780
861
  features: Features
781
862
  ): Promise<ProtocolV2InstallSource[]> {
863
+ if (this.params.preparedPlan) {
864
+ const requestedTargets = new Set(
865
+ this.params.preparedPlan.targetsToUpdate.filter(
866
+ (target): target is Exclude<FirmwareUpdateV4Target, 'resource'> => target !== 'resource'
867
+ )
868
+ );
869
+ const installSources: ProtocolV2InstallSource[] = [];
870
+ const preparedTargets = new Set<Exclude<FirmwareUpdateV4Target, 'resource'>>();
871
+ for (const artifact of this.params.preparedPlan.artifacts) {
872
+ if (artifact.target !== 'resource') {
873
+ const target = artifact.target as Exclude<FirmwareUpdateV4Target, 'resource'>;
874
+ const installTarget = PROTOCOL_V2_INSTALL_TARGET_BY_UPDATE_TARGET.get(target);
875
+ if (
876
+ !requestedTargets.has(target) ||
877
+ artifact.role !== 'component' ||
878
+ artifact.container !== 'raw' ||
879
+ !installTarget ||
880
+ preparedTargets.has(target)
881
+ ) {
882
+ throw ERRORS.TypedError(
883
+ HardwareErrorCode.RuntimeError,
884
+ `Protocol V2 prepared component artifact is invalid: ${artifact.artifactId}`,
885
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
886
+ );
887
+ }
888
+ installSources.push({
889
+ ...installTarget,
890
+ source: await this.openProtocolV2PreparedSource(artifact.artifact),
891
+ });
892
+ preparedTargets.add(target);
893
+ }
894
+ }
895
+ const missingTarget = Array.from(requestedTargets).find(
896
+ target => !preparedTargets.has(target)
897
+ );
898
+ if (missingTarget) {
899
+ throw ERRORS.TypedError(
900
+ HardwareErrorCode.RuntimeError,
901
+ `Protocol V2 ${missingTarget} artifact is not prepared`,
902
+ {
903
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
904
+ artifactName: missingTarget,
905
+ }
906
+ );
907
+ }
908
+ return installSources;
909
+ }
910
+
782
911
  const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
783
912
  if (!release) {
784
913
  throw ERRORS.TypedError(
@@ -828,6 +957,116 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
828
957
  return installSources;
829
958
  }
830
959
 
960
+ private async prepareProtocolV2LocalResourceArchive(
961
+ binary: ArrayBuffer
962
+ ): Promise<ProtocolV2ResourceBundleBinary[]> {
963
+ const zip = await JSZip.loadAsync(binary);
964
+ const entries = Object.values(zip.files).filter(entry => !entry.dir);
965
+ if (entries.length === 0 || entries.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT + 1) {
966
+ throw ERRORS.TypedError(
967
+ HardwareErrorCode.RuntimeError,
968
+ 'Protocol V2 local resource ZIP entry set is invalid',
969
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
970
+ );
971
+ }
972
+ const manifestEntry = zip.file('manifest.json');
973
+ if (!manifestEntry) {
974
+ throw ERRORS.TypedError(
975
+ HardwareErrorCode.RuntimeError,
976
+ 'Protocol V2 local resource ZIP has no manifest.json',
977
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
978
+ );
979
+ }
980
+ const manifestBinary = await manifestEntry.async('arraybuffer');
981
+ if (
982
+ manifestBinary.byteLength <= 0 ||
983
+ manifestBinary.byteLength > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
984
+ ) {
985
+ throw ERRORS.TypedError(
986
+ HardwareErrorCode.RuntimeError,
987
+ 'Protocol V2 local resource manifest size is invalid',
988
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
989
+ );
990
+ }
991
+
992
+ let manifestValue: unknown;
993
+ try {
994
+ manifestValue = JSON.parse(new TextDecoder().decode(manifestBinary));
995
+ } catch (error) {
996
+ throw ERRORS.TypedError(
997
+ HardwareErrorCode.RuntimeError,
998
+ `Protocol V2 local resource manifest is invalid: ${String(error)}`,
999
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
1000
+ );
1001
+ }
1002
+ const manifest = parseProtocolV2ResourceManifest(manifestValue);
1003
+ const selectedFiles = selectProtocolV2ResourceManifestFiles({
1004
+ manifest,
1005
+ targetsToUpdate: this.params.targetsToUpdate ?? [],
1006
+ });
1007
+ const expectedEntryNames = new Set([
1008
+ 'manifest.json',
1009
+ ...selectedFiles.map(file => file.archive_path),
1010
+ ]);
1011
+ if (
1012
+ entries.length !== expectedEntryNames.size ||
1013
+ entries.some(entry => !expectedEntryNames.has(entry.name))
1014
+ ) {
1015
+ throw ERRORS.TypedError(
1016
+ HardwareErrorCode.RuntimeError,
1017
+ 'Protocol V2 local resource ZIP contains an unexpected entry',
1018
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
1019
+ );
1020
+ }
1021
+
1022
+ let totalSize = 0;
1023
+ const resourceBundles: ProtocolV2ResourceBundleBinary[] = [];
1024
+ for (const [index, file] of selectedFiles.entries()) {
1025
+ const entry = zip.file(file.archive_path);
1026
+ if (!entry) {
1027
+ throw ERRORS.TypedError(
1028
+ HardwareErrorCode.RuntimeError,
1029
+ `Protocol V2 local resource ZIP is missing ${file.archive_path}`,
1030
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
1031
+ );
1032
+ }
1033
+ const fileBinary = await entry.async('arraybuffer');
1034
+ const digest = bytesToHex(sha256(new Uint8Array(fileBinary)));
1035
+ if (fileBinary.byteLength !== file.size || digest !== file.sha256.toLowerCase()) {
1036
+ throw ERRORS.TypedError(
1037
+ HardwareErrorCode.RuntimeError,
1038
+ `Protocol V2 local resource file does not match manifest: ${file.archive_path}`,
1039
+ { firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
1040
+ );
1041
+ }
1042
+ totalSize += fileBinary.byteLength;
1043
+ if (totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
1044
+ throw ERRORS.TypedError(
1045
+ HardwareErrorCode.RuntimeError,
1046
+ 'Protocol V2 local resource ZIP exceeds the total size limit',
1047
+ { firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
1048
+ );
1049
+ }
1050
+ const header =
1051
+ fileBinary.byteLength >= PROTOCOL_V2_OKPP_HEADER_SIZE
1052
+ ? parseProtocolV2OkppHeader(new Uint8Array(fileBinary))
1053
+ : null;
1054
+ resourceBundles.push({
1055
+ name: file.original_name || `resource-${index}`,
1056
+ binary: fileBinary,
1057
+ devicePath: file.device_path,
1058
+ ...(header
1059
+ ? {
1060
+ version: header.version,
1061
+ payloadHash: header.payloadHash,
1062
+ headerHash: header.headerHash,
1063
+ }
1064
+ : {}),
1065
+ });
1066
+ }
1067
+ return resourceBundles;
1068
+ }
1069
+
831
1070
  private async prepareProtocolV2ResourceSources(): Promise<ProtocolV2ResourceBundleSource[]> {
832
1071
  const resourceRequested = this.params.targetsToUpdate?.includes('resource') ?? false;
833
1072
  if (!resourceRequested) {
@@ -1669,10 +1908,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1669
1908
  fwBinaryMap,
1670
1909
  bootloaderBinary,
1671
1910
  installItems,
1911
+ resourceBundles = [],
1672
1912
  }: {
1673
1913
  fwBinaryMap?: ProtocolV2TargetBinary[];
1674
1914
  bootloaderBinary?: ArrayBuffer | null;
1675
1915
  installItems?: ProtocolV2InstallItem[];
1916
+ resourceBundles?: ProtocolV2ResourceBundleBinary[];
1676
1917
  }) {
1677
1918
  const memoryInstallItems =
1678
1919
  installItems ??
@@ -1689,9 +1930,19 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
1689
1930
  kind: item.kind,
1690
1931
  }))
1691
1932
  );
1933
+ const resourceSources = await Promise.all(
1934
+ resourceBundles.map(async resource => ({
1935
+ name: resource.name,
1936
+ source: await this.openProtocolV2MemorySource(resource.binary),
1937
+ devicePath: resource.devicePath,
1938
+ ...(resource.version ? { version: resource.version } : {}),
1939
+ ...(resource.payloadHash ? { payloadHash: resource.payloadHash } : {}),
1940
+ ...(resource.headerHash ? { headerHash: resource.headerHash } : {}),
1941
+ }))
1942
+ );
1692
1943
  return await this.executeProtocolV2Phases({
1693
1944
  installSources,
1694
- resourceSources: [],
1945
+ resourceSources,
1695
1946
  });
1696
1947
  } finally {
1697
1948
  await this.closeProtocolV2PreparedSources();
@@ -6,7 +6,6 @@ import type { CoreApi } from '../../types/api';
6
6
  import type {
7
7
  FirmwareArtifactReader,
8
8
  FirmwareArtifactReference,
9
- FirmwareUpdateV4Target,
10
9
  } from '../../types/api/firmwareUpdate';
11
10
  import type { FirmwareUpdatePlan } from '../../types/api/firmwareUpdatePlan';
12
11
 
@@ -24,33 +23,11 @@ export type FirmwareMemoryArtifact = {
24
23
  export type FirmwareUpdateV4MemoryHost = {
25
24
  preparedPlan: ReturnType<CoreApi['prepareFirmwareUpdatePlan']>;
26
25
  hostBindingGeneration: number;
27
- targetsToUpdate: FirmwareUpdateV4Target[];
28
- expectedDeviceId: string;
29
- expectedTargetVersions: Partial<Record<FirmwareUpdateV4Target, string>>;
30
- componentArtifacts: Partial<
31
- Record<Exclude<FirmwareUpdateV4Target, 'resource'>, FirmwareArtifactReference>
32
- >;
33
26
  release: () => void;
34
27
  };
35
28
 
36
29
  let memoryHostSequence = 0;
37
30
 
38
- const FIRMWARE_UPDATE_V4_COMPONENT_TARGETS = new Set<Exclude<FirmwareUpdateV4Target, 'resource'>>([
39
- 'boot',
40
- 'app_v1',
41
- 'app_v2',
42
- 'coprocessor',
43
- 'se01',
44
- 'se02',
45
- 'se03',
46
- 'se04',
47
- ]);
48
-
49
- const isFirmwareUpdateV4ComponentTarget = (
50
- target: string
51
- ): target is Exclude<FirmwareUpdateV4Target, 'resource'> =>
52
- FIRMWARE_UPDATE_V4_COMPONENT_TARGETS.has(target as Exclude<FirmwareUpdateV4Target, 'resource'>);
53
-
54
31
  const createReference = (binary: ArrayBuffer, prefix: string): FirmwareArtifactReference => {
55
32
  const digest = bytesToHex(sha256(new Uint8Array(binary)));
56
33
  return {
@@ -148,23 +125,9 @@ export function prepareFirmwareUpdateV4MemoryHost({
148
125
  artifactReader,
149
126
  preparedPlanDigest: preparedPlan.preparedPlanDigest,
150
127
  });
151
- const componentArtifacts: FirmwareUpdateV4MemoryHost['componentArtifacts'] = {};
152
- const expectedTargetVersions: FirmwareUpdateV4MemoryHost['expectedTargetVersions'] = {};
153
- for (const artifact of preparedPlan.artifacts) {
154
- if (artifact.role === 'component' && isFirmwareUpdateV4ComponentTarget(artifact.target)) {
155
- componentArtifacts[artifact.target] = artifact.artifact;
156
- }
157
- if (artifact.targetVersion) {
158
- expectedTargetVersions[artifact.target as FirmwareUpdateV4Target] = artifact.targetVersion;
159
- }
160
- }
161
128
  return {
162
129
  preparedPlan,
163
130
  hostBindingGeneration,
164
- targetsToUpdate: [...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[],
165
- expectedDeviceId: preparedPlan.deviceIdentity,
166
- expectedTargetVersions,
167
- componentArtifacts,
168
131
  release: () => {
169
132
  sdk.unregisterFirmwareUpdateHostBinding(hostBindingGeneration);
170
133
  readers.clear();
@@ -154,6 +154,8 @@ export interface FirmwareUpdateV4Params {
154
154
  se02Binary?: ArrayBuffer;
155
155
  se03Binary?: ArrayBuffer;
156
156
  se04Binary?: ArrayBuffer;
157
+ /** Complete Protocol V2 resource ZIP for local development; never bound to a remote Plan. */
158
+ resourceArchiveBinary?: ArrayBuffer;
157
159
  forcedUpdateRes?: boolean;
158
160
  artifactReader?: FirmwareArtifactReader;
159
161
  componentArtifacts?: Partial<