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

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 (58) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +69 -26
  2. package/__tests__/check-firmware-release-protocol-v2.test.ts +1 -2
  3. package/__tests__/firmware-memory-host.test.ts +108 -0
  4. package/__tests__/firmware-update/device-update-bootloader.test.ts +4 -1
  5. package/__tests__/firmware-update/firmware-host-binding.test.ts +9 -0
  6. package/__tests__/firmware-update/firmware-update-plan.test.ts +15 -70
  7. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
  8. package/__tests__/protocol-v2-resources.test.ts +59 -39
  9. package/__tests__/protocol-v2.test.ts +255 -306
  10. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  11. package/dist/api/FirmwareUpdateV4.d.ts +1 -2
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/firmware/FirmwareHostBinding.d.ts +1 -1
  14. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -1
  15. package/dist/api/firmware/FirmwareMemoryHost.d.ts +27 -0
  16. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  17. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +3 -4
  18. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  19. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  20. package/dist/api/firmware/getBinary.d.ts +0 -1
  21. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  22. package/dist/data-manager/DataManager.d.ts +3 -1
  23. package/dist/data-manager/DataManager.d.ts.map +1 -1
  24. package/dist/index.d.ts +32 -70
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +450 -390
  27. package/dist/inject.d.ts.map +1 -1
  28. package/dist/protocols/protocol-v2/resources.d.ts +0 -17
  29. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  30. package/dist/types/api/checkAllFirmwareRelease.d.ts +0 -1
  31. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  32. package/dist/types/api/export.d.ts +0 -1
  33. package/dist/types/api/export.d.ts.map +1 -1
  34. package/dist/types/api/firmwareUpdate.d.ts +1 -10
  35. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  36. package/dist/types/api/index.d.ts +0 -2
  37. package/dist/types/api/index.d.ts.map +1 -1
  38. package/dist/types/settings.d.ts +3 -15
  39. package/dist/types/settings.d.ts.map +1 -1
  40. package/package.json +4 -4
  41. package/src/api/CheckAllFirmwareRelease.ts +4 -13
  42. package/src/api/FirmwareUpdateV4.ts +166 -170
  43. package/src/api/firmware/FirmwareHostBinding.ts +16 -3
  44. package/src/api/firmware/FirmwareMemoryHost.ts +174 -0
  45. package/src/api/firmware/FirmwareUpdatePlan.ts +28 -48
  46. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +8 -1
  47. package/src/data-manager/DataManager.ts +33 -13
  48. package/src/index.ts +6 -1
  49. package/src/inject.ts +0 -2
  50. package/src/protocols/protocol-v2/resources.ts +0 -47
  51. package/src/types/api/checkAllFirmwareRelease.ts +0 -1
  52. package/src/types/api/export.ts +0 -4
  53. package/src/types/api/firmwareUpdate.ts +2 -15
  54. package/src/types/api/index.ts +0 -2
  55. package/src/types/settings.ts +3 -25
  56. package/dist/types/api/protocolV2ResourceManifest.d.ts +0 -17
  57. package/dist/types/api/protocolV2ResourceManifest.d.ts.map +0 -1
  58. package/src/types/api/protocolV2ResourceManifest.ts +0 -19
@@ -0,0 +1,174 @@
1
+ import { sha256 } from '@noble/hashes/sha256';
2
+ import { bytesToHex } from '@noble/hashes/utils';
3
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
4
+
5
+ import type { CoreApi } from '../../types/api';
6
+ import type {
7
+ FirmwareArtifactReader,
8
+ FirmwareArtifactReference,
9
+ FirmwareUpdateV4Target,
10
+ } from '../../types/api/firmwareUpdate';
11
+ import type { FirmwareUpdatePlan } from '../../types/api/firmwareUpdatePlan';
12
+
13
+ export type FirmwareMemoryArtifactEntry = {
14
+ entryName: string;
15
+ binary: ArrayBuffer;
16
+ };
17
+
18
+ export type FirmwareMemoryArtifact = {
19
+ artifactId: string;
20
+ binary: ArrayBuffer;
21
+ materializedEntries?: FirmwareMemoryArtifactEntry[];
22
+ };
23
+
24
+ export type FirmwareUpdateV4MemoryHost = {
25
+ preparedPlan: ReturnType<CoreApi['prepareFirmwareUpdatePlan']>;
26
+ 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
+ release: () => void;
34
+ };
35
+
36
+ let memoryHostSequence = 0;
37
+
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
+ const createReference = (binary: ArrayBuffer, prefix: string): FirmwareArtifactReference => {
55
+ const digest = bytesToHex(sha256(new Uint8Array(binary)));
56
+ return {
57
+ artifactRef: `fwmem:${prefix}:${digest.slice(0, 32)}`,
58
+ size: binary.byteLength,
59
+ sha256: digest,
60
+ };
61
+ };
62
+
63
+ export function prepareFirmwareUpdateV4MemoryHost({
64
+ sdk,
65
+ plan,
66
+ artifacts,
67
+ }: {
68
+ sdk: Pick<
69
+ CoreApi,
70
+ | 'prepareFirmwareUpdatePlan'
71
+ | 'registerFirmwareUpdateHostBinding'
72
+ | 'unregisterFirmwareUpdateHostBinding'
73
+ >;
74
+ plan: FirmwareUpdatePlan;
75
+ artifacts: FirmwareMemoryArtifact[];
76
+ }): FirmwareUpdateV4MemoryHost {
77
+ if (plan.executor !== 'v4') {
78
+ throw ERRORS.TypedError(
79
+ HardwareErrorCode.RuntimeError,
80
+ 'Firmware memory host only supports V4 plans'
81
+ );
82
+ }
83
+ memoryHostSequence += 1;
84
+ const hostId = `${Date.now()}:${memoryHostSequence}`;
85
+ const binaries = new Map<string, Uint8Array>();
86
+ const inputs = artifacts.map((input, artifactIndex) => {
87
+ const artifact = createReference(input.binary, `${hostId}:artifact:${artifactIndex}`);
88
+ binaries.set(artifact.artifactRef, new Uint8Array(input.binary));
89
+ const materializedEntries = input.materializedEntries?.map((entry, entryIndex) => {
90
+ const entryArtifact = createReference(
91
+ entry.binary,
92
+ `${hostId}:entry:${artifactIndex}:${entryIndex}`
93
+ );
94
+ binaries.set(entryArtifact.artifactRef, new Uint8Array(entry.binary));
95
+ return {
96
+ entryName: entry.entryName,
97
+ artifact: entryArtifact,
98
+ };
99
+ });
100
+ return {
101
+ artifactId: input.artifactId,
102
+ artifact,
103
+ ...(materializedEntries?.length ? { materializedEntries } : {}),
104
+ };
105
+ });
106
+ const preparedPlan = sdk.prepareFirmwareUpdatePlan({
107
+ plan,
108
+ leaseRef: `fwmemlease:${hostId}`,
109
+ artifacts: inputs,
110
+ });
111
+ const readers = new Map<string, Uint8Array>();
112
+ let readerSequence = 0;
113
+ const artifactReader: FirmwareArtifactReader = {
114
+ open({ artifactRef }) {
115
+ const binary = binaries.get(artifactRef);
116
+ if (!binary) {
117
+ throw ERRORS.TypedError(
118
+ HardwareErrorCode.RuntimeError,
119
+ 'Firmware memory artifact is unavailable'
120
+ );
121
+ }
122
+ readerSequence += 1;
123
+ const readerId = `fwmemreader:${hostId}:${readerSequence}`;
124
+ readers.set(readerId, binary);
125
+ return Promise.resolve({ readerId, size: binary.byteLength });
126
+ },
127
+ read({ readerId, offset, length }) {
128
+ const binary = readers.get(readerId);
129
+ if (!binary || offset < 0 || length <= 0 || offset + length > binary.byteLength) {
130
+ throw ERRORS.TypedError(
131
+ HardwareErrorCode.RuntimeError,
132
+ 'Firmware memory artifact read is invalid'
133
+ );
134
+ }
135
+ const data = binary.slice(offset, offset + length).buffer;
136
+ return Promise.resolve({
137
+ data,
138
+ bytesRead: data.byteLength,
139
+ eof: offset + length === binary.byteLength,
140
+ });
141
+ },
142
+ close({ readerId }) {
143
+ readers.delete(readerId);
144
+ return Promise.resolve();
145
+ },
146
+ };
147
+ const hostBindingGeneration = sdk.registerFirmwareUpdateHostBinding({
148
+ artifactReader,
149
+ preparedPlanDigest: preparedPlan.preparedPlanDigest,
150
+ });
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
+ return {
162
+ preparedPlan,
163
+ hostBindingGeneration,
164
+ targetsToUpdate: [...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[],
165
+ expectedDeviceId: preparedPlan.deviceIdentity,
166
+ expectedTargetVersions,
167
+ componentArtifacts,
168
+ release: () => {
169
+ sdk.unregisterFirmwareUpdateHostBinding(hostBindingGeneration);
170
+ readers.clear();
171
+ binaries.clear();
172
+ },
173
+ };
174
+ }
@@ -17,7 +17,7 @@ import type {
17
17
  FirmwareUpdatePlanForceTarget,
18
18
  FirmwareUpdatePlanTarget,
19
19
  } from '../../types/api/firmwareUpdatePlan';
20
- import type { Features } from '../../types';
20
+ import type { Features, IProtocolV2ResourceSource } from '../../types';
21
21
 
22
22
  type FirmwareUpdatePlatform = 'native' | 'desktop' | 'ext' | 'web' | 'web-embed';
23
23
 
@@ -31,7 +31,6 @@ type ReleaseRecord = {
31
31
  version?: unknown;
32
32
  components?: unknown;
33
33
  installOrder?: unknown;
34
- resourceBundles?: unknown;
35
34
  fingerprint?: unknown;
36
35
  fingerprintWeb?: unknown;
37
36
  expectedSize?: unknown;
@@ -444,11 +443,9 @@ const buildProtocolV2Artifacts = (
444
443
  release: ReleaseRecord,
445
444
  {
446
445
  includeComponents = true,
447
- includeResources = true,
448
446
  componentTargets: selectedComponentTargets,
449
447
  }: {
450
448
  includeComponents?: boolean;
451
- includeResources?: boolean;
452
449
  componentTargets?: ReadonlySet<FirmwareUpdatePlanTarget>;
453
450
  } = {}
454
451
  ): {
@@ -523,43 +520,6 @@ const buildProtocolV2Artifacts = (
523
520
  targets.push(target);
524
521
  }
525
522
 
526
- const bundles =
527
- includeResources && Array.isArray(release.resourceBundles) ? release.resourceBundles : [];
528
- const resourceBundleNames = new Set<string>();
529
- for (const value of bundles) {
530
- const bundle = asRecord(value);
531
- const name = asString(bundle?.name);
532
- if (!bundle || !name) {
533
- throw ERRORS.TypedError(
534
- HardwareErrorCode.RuntimeError,
535
- 'Protocol V2 resource bundle is invalid',
536
- { firmwareUpdateCode: 'FirmwarePlanInvalid' }
537
- );
538
- }
539
- if (resourceBundleNames.has(name)) {
540
- throw ERRORS.TypedError(
541
- HardwareErrorCode.RuntimeError,
542
- `Protocol V2 resource bundle duplicates name ${name}`,
543
- { firmwareUpdateCode: 'FirmwarePlanInvalid' }
544
- );
545
- }
546
- resourceBundleNames.add(name);
547
- artifacts.push({
548
- artifactId: `resourceBundle:${name}`,
549
- role: 'resourceBundle',
550
- target: 'resource',
551
- url: assertArtifactUrl(bundle.url, `Protocol V2 resource bundle ${name}`),
552
- container: 'raw',
553
- logicalName: name,
554
- ...asIntegrity({
555
- size: bundle.expectedSize,
556
- sha256: bundle.fingerprint,
557
- }),
558
- });
559
- }
560
- if (bundles.length > 0) {
561
- targets.push('resource');
562
- }
563
523
  return { artifacts, targets: [...new Set(targets)] };
564
524
  };
565
525
 
@@ -679,7 +639,7 @@ export const buildProtocolV2FirmwareUpdatePlan = ({
679
639
  release,
680
640
  targetsToUpdate,
681
641
  forceUpdateTargets,
682
- resourceArchiveAvailable,
642
+ resourceArchive,
683
643
  }: {
684
644
  features: Features;
685
645
  firmwareType: EFirmwareType;
@@ -687,7 +647,7 @@ export const buildProtocolV2FirmwareUpdatePlan = ({
687
647
  release: ReleaseRecord | undefined;
688
648
  targetsToUpdate: readonly FirmwareUpdateV4Target[];
689
649
  forceUpdateTargets?: FirmwareUpdatePlanForceTarget[];
690
- resourceArchiveAvailable: boolean;
650
+ resourceArchive: IProtocolV2ResourceSource | undefined;
691
651
  }): FirmwareUpdatePlan => {
692
652
  const validatedForceTargets = validateFirmwareUpdatePlanForceTargets(forceUpdateTargets);
693
653
  if (validatedForceTargets.some(target => target === 'ble' || target === 'bootloader')) {
@@ -705,9 +665,32 @@ export const buildProtocolV2FirmwareUpdatePlan = ({
705
665
  }
706
666
  const protocolV2 = buildProtocolV2Artifacts(release ?? {}, {
707
667
  includeComponents: requestedComponentTargets.size > 0,
708
- includeResources: false,
709
668
  componentTargets: requestedComponentTargets,
710
669
  });
670
+ const resourceRequested = targetsToUpdate.includes('resource');
671
+ if (resourceRequested) {
672
+ const archive = resourceArchive;
673
+ if (!archive) {
674
+ return planError('Protocol V2 resource target has no resource archive');
675
+ }
676
+ const integrity = asIntegrity({
677
+ size: archive.archiveSize,
678
+ sha256: archive.archiveSha256,
679
+ });
680
+ if (integrity.expectedSize === undefined || integrity.expectedSha256 === undefined) {
681
+ planError('Protocol V2 resource archive integrity metadata is invalid');
682
+ }
683
+ protocolV2.artifacts.push({
684
+ artifactId: 'resource:archive',
685
+ role: 'resourceBundle',
686
+ target: 'resource',
687
+ url: assertArtifactUrl(archive.archiveUrl, 'Protocol V2 resource archive'),
688
+ container: 'zip',
689
+ logicalName: 'protocol-v2-resource-archive',
690
+ ...integrity,
691
+ });
692
+ protocolV2.targets.push('resource');
693
+ }
711
694
  const representedTargets = new Set(protocolV2.targets);
712
695
  const missingTarget = Array.from(requestedComponentTargets).find(
713
696
  target => !representedTargets.has(target)
@@ -718,12 +701,10 @@ export const buildProtocolV2FirmwareUpdatePlan = ({
718
701
  if (validatedForceTargets.includes('firmware') && protocolV2.targets.length === 0) {
719
702
  planError('Forced firmware update target firmware is not represented by the plan');
720
703
  }
721
- if (validatedForceTargets.includes('resource') && !resourceArchiveAvailable) {
704
+ if (validatedForceTargets.includes('resource') && !resourceArchive) {
722
705
  planError('Forced firmware update target resource has no Protocol V2 resource archive');
723
706
  }
724
707
 
725
- // Protocol V2 resource archives are materialized by the host into explicit
726
- // resourceFiles. The prepared artifact plan therefore binds firmware components only.
727
708
  return finalizeFirmwareUpdatePlan({
728
709
  features,
729
710
  firmwareType,
@@ -781,7 +762,6 @@ export const buildFirmwareUpdatePlan = ({
781
762
  if (executor === 'v4' && (shouldUpdateFirmware || shouldUpdateResource)) {
782
763
  const protocolV2 = buildProtocolV2Artifacts(firmwareRelease ?? {}, {
783
764
  includeComponents: shouldUpdateFirmware,
784
- includeResources: shouldUpdateResource,
785
765
  });
786
766
  artifacts = protocolV2.artifacts;
787
767
  targetsToUpdate = protocolV2.targets;
@@ -278,7 +278,14 @@ export const validateFirmwareUpdatePreparedPlan = (value: unknown): FirmwareUpda
278
278
  artifactIds.add(artifact.artifactId);
279
279
  artifactTargets.add(artifact.target);
280
280
  assertFirmwareArtifactReference(artifact.artifact);
281
- artifact.materializedEntries?.forEach(assertPreparedEntry);
281
+ const materializedEntryNames =
282
+ artifact.materializedEntries?.map(entry => {
283
+ assertPreparedEntry(entry);
284
+ return getFirmwareUpdateResourceName(entry.entryName).toLowerCase();
285
+ }) ?? [];
286
+ if (new Set(materializedEntryNames).size !== materializedEntryNames.length) {
287
+ return preparedPlanError('Firmware prepared plan contains duplicate entry names');
288
+ }
282
289
  }
283
290
  if (
284
291
  preparedPlan.targetsToUpdate.some(target => !FIRMWARE_UPDATE_PLAN_TARGETS.has(target)) ||
@@ -94,12 +94,10 @@ export default class DataManager {
94
94
  ble: [],
95
95
  },
96
96
  [EDeviceType.Pro2]: {
97
- firmware: [],
98
- ble: [],
97
+ 'firmware-v1': [],
99
98
  },
100
99
  [EDeviceType.Neo]: {
101
- firmware: [],
102
- ble: [],
100
+ 'firmware-v1': [],
103
101
  },
104
102
  [EDeviceType.ClassicPure]: {
105
103
  firmware: [],
@@ -121,6 +119,8 @@ export default class DataManager {
121
119
 
122
120
  static protocolV2ResourcesConfigError: Error | undefined;
123
121
 
122
+ private static protocolV2NeoResourcesConfigError: Error | undefined;
123
+
124
124
  static getFirmwareStatus = (
125
125
  features: Features,
126
126
  firmwareType: EFirmwareType
@@ -410,6 +410,7 @@ export default class DataManager {
410
410
  static async load(settings: ConnectSettings): Promise<boolean> {
411
411
  this.settings = settings;
412
412
  this.protocolV2ResourcesConfigError = undefined;
413
+ this.protocolV2NeoResourcesConfigError = undefined;
413
414
  const manifestMode =
414
415
  settings.firmwareManifestMode ?? (settings.fetchConfig ? 'sdk-managed' : undefined);
415
416
  if (settings.preloadedConfig) {
@@ -476,15 +477,21 @@ export default class DataManager {
476
477
  pro2Resources = parseProtocolV2Resources(
477
478
  (data.pro2 as { resources?: unknown } | undefined)?.resources
478
479
  );
479
- neoResources = parseProtocolV2Resources(
480
- (data.neo as { resources?: unknown } | undefined)?.resources
481
- );
482
480
  } catch (error) {
483
481
  // Firmware resource metadata is not required for base communication. If the
484
482
  // remote config is temporarily incomplete, disable this resource update only.
485
483
  this.protocolV2ResourcesConfigError =
486
484
  error instanceof Error ? error : new Error(String(error));
487
- Log.warn('[DataConfig] Ignoring invalid Protocol V2 resources config:', error);
485
+ Log.warn('[DataConfig] Ignoring invalid Pro2 resources config:', error);
486
+ }
487
+ try {
488
+ neoResources = parseProtocolV2Resources(
489
+ (data.neo as { resources?: unknown } | undefined)?.resources
490
+ );
491
+ } catch (error) {
492
+ this.protocolV2NeoResourcesConfigError =
493
+ error instanceof Error ? error : new Error(String(error));
494
+ Log.warn('[DataConfig] Ignoring invalid Neo resources config:', error);
488
495
  }
489
496
  const enrichedPro2Config = this.enrichFirmwareReleaseInfo(data.pro2);
490
497
  const enrichedNeoConfig = this.enrichFirmwareReleaseInfo(data.neo);
@@ -537,7 +544,12 @@ export default class DataManager {
537
544
  /** Force a fresh remote config before an update is allowed to mutate the device. */
538
545
  static async forceReloadData({
539
546
  requireResources = false,
540
- }: { requireResources?: boolean } = {}): Promise<void> {
547
+ resourceDeviceType = EDeviceType.Pro2,
548
+ }: {
549
+ requireResources?: boolean;
550
+ resourceDeviceType?: EDeviceType.Pro2 | EDeviceType.Neo;
551
+ } = {}): Promise<void> {
552
+ const resourceDeviceName = resourceDeviceType === EDeviceType.Pro2 ? 'Pro2' : 'Neo';
541
553
  if (!this.settings) {
542
554
  throw new Error('Remote config settings are not initialized');
543
555
  }
@@ -545,10 +557,14 @@ export default class DataManager {
545
557
  this.lastCheckTimestamp > 0 &&
546
558
  getTimeStamp() - this.lastCheckTimestamp <= FIRMWARE_UPDATE_CONFIG_FRESHNESS_MS;
547
559
  if (hasFreshConfig) {
548
- if (requireResources && this.protocolV2ResourcesConfigError) {
560
+ const resourcesConfigError =
561
+ resourceDeviceType === EDeviceType.Pro2
562
+ ? this.protocolV2ResourcesConfigError
563
+ : this.protocolV2NeoResourcesConfigError;
564
+ if (requireResources && resourcesConfigError) {
549
565
  throw ERRORS.TypedError(
550
566
  HardwareErrorCode.FirmwareUpdateDownloadFailed,
551
- `Invalid Pro2 resources config: ${this.protocolV2ResourcesConfigError.message}`
567
+ `Invalid ${resourceDeviceName} resources config: ${resourcesConfigError.message}`
552
568
  );
553
569
  }
554
570
  return;
@@ -560,10 +576,14 @@ export default class DataManager {
560
576
  'Unable to refresh the latest remote config'
561
577
  );
562
578
  }
563
- if (requireResources && this.protocolV2ResourcesConfigError) {
579
+ const resourcesConfigError =
580
+ resourceDeviceType === EDeviceType.Pro2
581
+ ? this.protocolV2ResourcesConfigError
582
+ : this.protocolV2NeoResourcesConfigError;
583
+ if (requireResources && resourcesConfigError) {
564
584
  throw ERRORS.TypedError(
565
585
  HardwareErrorCode.FirmwareUpdateDownloadFailed,
566
- `Invalid Pro2 resources config: ${this.protocolV2ResourcesConfigError.message}`
586
+ `Invalid ${resourceDeviceName} resources config: ${resourcesConfigError.message}`
567
587
  );
568
588
  }
569
589
  this.lastCheckTimestamp = getTimeStamp();
package/src/index.ts CHANGED
@@ -23,9 +23,14 @@ export { projectFeatures as projectDeviceStateFeatures } from './device/DeviceSt
23
23
  export { getMethodSupportedProtocols } from './api/utils';
24
24
  export {
25
25
  parseProtocolV2ResourceManifest,
26
- prepareProtocolV2ResourceFiles,
27
26
  selectProtocolV2ResourceManifestFiles,
28
27
  } from './protocols/protocol-v2/resources';
28
+ export { prepareFirmwareUpdateV4MemoryHost } from './api/firmware/FirmwareMemoryHost';
29
+ export type {
30
+ FirmwareMemoryArtifact,
31
+ FirmwareMemoryArtifactEntry,
32
+ FirmwareUpdateV4MemoryHost,
33
+ } from './api/firmware/FirmwareMemoryHost';
29
34
  export {
30
35
  getFirmwareUpdateHostBindingGeneration,
31
36
  registerFirmwareUpdateHostBinding,
package/src/inject.ts CHANGED
@@ -3,7 +3,6 @@ import {
3
3
  prepareFirmwareUpdatePlan,
4
4
  validateFirmwareUpdatePreparedPlan,
5
5
  } from './api/firmware/FirmwareUpdatePreparedPlan';
6
- import { prepareProtocolV2ResourceFiles } from './protocols/protocol-v2/resources';
7
6
  import {
8
7
  getFirmwareUpdateHostBindingGeneration,
9
8
  registerFirmwareUpdateHostBinding,
@@ -169,7 +168,6 @@ export const createCoreApi = (
169
168
  getFirmwareUpdateHostBindingGeneration,
170
169
  prepareFirmwareUpdatePlan,
171
170
  validateFirmwareUpdatePreparedPlan,
172
- prepareProtocolV2ResourceFiles,
173
171
  getLogs: () => call({ method: 'getLogs' }),
174
172
  clearSessionCache: params => call({ ...params, method: 'clearSessionCache' }),
175
173
  /**
@@ -1,5 +1,3 @@
1
- import { sha256 } from '@noble/hashes/sha256';
2
-
3
1
  import type {
4
2
  IProtocolV2ResourceManifest,
5
3
  IProtocolV2ResourceManifestFile,
@@ -210,48 +208,3 @@ export function selectProtocolV2ResourceManifestFiles({
210
208
  }): IProtocolV2ResourceManifestFile[] {
211
209
  return targetsToUpdate.includes('resource') ? [...manifest.files] : [];
212
210
  }
213
-
214
- export function prepareProtocolV2ResourceFiles({
215
- manifest: value,
216
- files,
217
- targetsToUpdate,
218
- }: {
219
- manifest: unknown;
220
- files: Array<{ archivePath: string; binary: ArrayBuffer }>;
221
- targetsToUpdate: readonly FirmwareUpdateV4Target[];
222
- }): Array<{ binary: ArrayBuffer; devicePath: string; size: number; fileHash: string }> {
223
- const manifest = parseProtocolV2ResourceManifest(value);
224
- const selected = selectProtocolV2ResourceManifestFiles({ manifest, targetsToUpdate });
225
- const binaries = new Map(files.map(file => [file.archivePath, file.binary] as const));
226
- return selected.map(file => {
227
- const binary = binaries.get(file.archive_path);
228
- if (
229
- !binary ||
230
- !isProtocolV2ResourceFileValid(binary, {
231
- size: file.size,
232
- fileHash: file.sha256,
233
- })
234
- ) {
235
- throw new Error(`Pro2 resource manifest file verification failed: ${file.archive_path}`);
236
- }
237
- return {
238
- binary,
239
- devicePath: file.device_path,
240
- size: file.size,
241
- fileHash: file.sha256,
242
- };
243
- });
244
- }
245
-
246
- function bytesToHex(bytes: Uint8Array): string {
247
- return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
248
- }
249
-
250
- /** Verify the complete downloaded file before any device mutation. */
251
- export function isProtocolV2ResourceFileValid(
252
- binary: ArrayBuffer,
253
- resource: { size: number; fileHash: string }
254
- ): boolean {
255
- if (binary.byteLength !== resource.size) return false;
256
- return bytesToHex(sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
257
- }
@@ -75,7 +75,6 @@ export type AllFirmwareRelease = {
75
75
  required?: boolean;
76
76
  resourceStatus?: 'valid' | 'outdated' | 'unknown';
77
77
  resourceArchive?: IProtocolV2ResourceSource;
78
- resourcePreparationRequired?: boolean;
79
78
  currentVersions?: DeviceStateVersions;
80
79
  components?: ProtocolV2FirmwareComponentRelease[];
81
80
  targetsToUpdate?: FirmwareUpdateV4Target[];
@@ -49,10 +49,6 @@ export type {
49
49
  FirmwareUpdatePreparedEntry,
50
50
  FirmwareUpdatePreparedPlan,
51
51
  } from './firmwareUpdatePreparedPlan';
52
- export type {
53
- ProtocolV2PreparedResourceFile,
54
- ProtocolV2ResourceManifestBinary,
55
- } from './protocolV2ResourceManifest';
56
52
  export type { FirmwareUpdateCapabilities } from './firmwareUpdateCapabilities';
57
53
  export type {
58
54
  AllFirmwareRelease,
@@ -26,6 +26,8 @@ export interface FirmwareArtifactReader {
26
26
 
27
27
  export interface FirmwareUpdateHostBinding {
28
28
  artifactReader: FirmwareArtifactReader;
29
+ /** 将读取器 generation 绑定到包含 ZIP 条目的完整 PreparedPlan。 */
30
+ preparedPlanDigest: string;
29
31
  }
30
32
 
31
33
  export interface FirmwareUpdateBinaryParams {
@@ -153,25 +155,10 @@ export interface FirmwareUpdateV4Params {
153
155
  se03Binary?: ArrayBuffer;
154
156
  se04Binary?: ArrayBuffer;
155
157
  forcedUpdateRes?: boolean;
156
- /**
157
- * Arbitrary Protocol V2 resource files written directly with FilesystemFileWrite.
158
- * Use this for every file selected from the manifest-driven resource archive.
159
- * When provided, these files are authoritative for the resource target.
160
- */
161
- resourceFiles?: Array<{
162
- binary: ArrayBuffer;
163
- devicePath: string;
164
- size?: number;
165
- fileHash?: string;
166
- }>;
167
158
  artifactReader?: FirmwareArtifactReader;
168
159
  componentArtifacts?: Partial<
169
160
  Record<Exclude<FirmwareUpdateV4Target, 'resource'>, FirmwareArtifactReference>
170
161
  >;
171
- resourceBundleArtifacts?: Array<{
172
- name: string;
173
- artifact: FirmwareArtifactReference;
174
- }>;
175
162
  }
176
163
 
177
164
  export declare function registerFirmwareUpdateHostBinding(
@@ -41,7 +41,6 @@ import type {
41
41
  prepareFirmwareUpdatePlan,
42
42
  validateFirmwareUpdatePreparedPlan,
43
43
  } from './firmwareUpdatePreparedPlan';
44
- import type { prepareProtocolV2ResourceFiles } from './protocolV2ResourceManifest';
45
44
  import type { promptWebDeviceAccess } from './promptWebDeviceAccess';
46
45
  import type { deviceReset } from './deviceReset';
47
46
  import type { deviceRecovery } from './deviceRecovery';
@@ -254,7 +253,6 @@ export type CoreApi = {
254
253
  getFirmwareUpdateCapabilities: typeof getFirmwareUpdateCapabilities;
255
254
  prepareFirmwareUpdatePlan: typeof prepareFirmwareUpdatePlan;
256
255
  validateFirmwareUpdatePreparedPlan: typeof validateFirmwareUpdatePreparedPlan;
257
- prepareProtocolV2ResourceFiles: typeof prepareProtocolV2ResourceFiles;
258
256
  cipherKeyValue: typeof cipherKeyValue;
259
257
 
260
258
  /**
@@ -104,24 +104,6 @@ export type IProtocolV2ResourceManifest = {
104
104
  files: IProtocolV2ResourceManifestFile[];
105
105
  };
106
106
 
107
- /** Pro2 RESC bundle okpkg descriptor for incremental FileWrite synchronization. */
108
- export type IProtocolV2ResourceBundle = {
109
- /** Bundle name, such as images, animation, translations, or fonts_roobert. */
110
- name: string;
111
- /** Download URL. */
112
- url: string;
113
- fingerprint?: string;
114
- expectedSize?: number;
115
- /** Device target path, such as vol0:/bundles/images/images.okpkg. */
116
- devicePath: string;
117
- /** okpkg payload_version used to skip matching content after FileRead. */
118
- version?: IVersionArray;
119
- /** okpkg payload_hash used for SHA3-512 comparison after FileRead. */
120
- payloadHash?: string;
121
- /** okpkg header_hash used for SHA3-512 comparison after FileRead. */
122
- headerHash?: string;
123
- };
124
-
125
107
  /** STM32 firmware config */
126
108
  export type IFirmwareReleaseInfo = {
127
109
  required: boolean;
@@ -149,8 +131,6 @@ export type IFirmwareReleaseInfo = {
149
131
  upgradeType?: 'payload-package-set' | string;
150
132
  components?: Record<string, IProtocolV2FirmwareComponent>;
151
133
  installOrder?: string[];
152
- /** Pro2 RESC bundles for incremental direct FileWrite synchronization. */
153
- resourceBundles?: IProtocolV2ResourceBundle[];
154
134
  bootloaderChangelog?: {
155
135
  [k in ILocale]: string;
156
136
  };
@@ -179,22 +159,20 @@ export type IBLEFirmwareReleaseInfo = {
179
159
  };
180
160
 
181
161
  type IKnownDevice = Exclude<IDeviceType, 'unknown'>;
182
- type ILegacyKnownDevice = Exclude<IKnownDevice, 'neo'>;
183
-
184
162
  type IDeviceReleaseInfo = {
185
- firmware: IFirmwareReleaseInfo[];
163
+ firmware?: IFirmwareReleaseInfo[];
186
164
  /** Protocol V2 payload package set */
187
165
  'firmware-v1'?: IFirmwareReleaseInfo[];
188
166
  'firmware-v2'?: IFirmwareReleaseInfo[];
189
167
  'firmware-v8'?: IFirmwareReleaseInfo[];
190
168
  'firmware-btc-v8'?: IFirmwareReleaseInfo[];
191
- ble: IBLEFirmwareReleaseInfo[];
169
+ ble?: IBLEFirmwareReleaseInfo[];
192
170
  /** Independent Protocol V2 resource release configuration. */
193
171
  resources?: IProtocolV2Resources;
194
172
  };
195
173
 
196
174
  export type DeviceTypeMap = {
197
- [k in ILegacyKnownDevice]: IDeviceReleaseInfo;
175
+ [k in Exclude<IKnownDevice, 'neo'>]: IDeviceReleaseInfo;
198
176
  } & {
199
177
  /** Optional until every remote-config producer publishes a Neo entry. */
200
178
  neo?: IDeviceReleaseInfo;
@@ -1,17 +0,0 @@
1
- import type { FirmwareUpdateV4Target } from './firmwareUpdate';
2
- export type ProtocolV2ResourceManifestBinary = {
3
- archivePath: string;
4
- binary: ArrayBuffer;
5
- };
6
- export type ProtocolV2PreparedResourceFile = {
7
- binary: ArrayBuffer;
8
- devicePath: string;
9
- size: number;
10
- fileHash: string;
11
- };
12
- export declare function prepareProtocolV2ResourceFiles(input: {
13
- manifest: unknown;
14
- files: ProtocolV2ResourceManifestBinary[];
15
- targetsToUpdate: readonly FirmwareUpdateV4Target[];
16
- }): ProtocolV2PreparedResourceFile[];
17
- //# sourceMappingURL=protocolV2ResourceManifest.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocolV2ResourceManifest.d.ts","sourceRoot":"","sources":["../../../src/types/api/protocolV2ResourceManifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE/D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,8BAA8B,CAAC,KAAK,EAAE;IAC5D,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,gCAAgC,EAAE,CAAC;IAC1C,eAAe,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACpD,GAAG,8BAA8B,EAAE,CAAC"}