@onekeyfe/hd-core 1.2.0-alpha.40 → 1.2.0-alpha.42

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.
package/dist/index.js CHANGED
@@ -10,8 +10,8 @@ var lodash = require('lodash');
10
10
  var ByteBuffer = require('bytebuffer');
11
11
  var blake2s = require('@noble/hashes/blake2s');
12
12
  var utils = require('@noble/hashes/utils');
13
- var BigNumber = require('bignumber.js');
14
13
  var sha256 = require('@noble/hashes/sha256');
14
+ var BigNumber = require('bignumber.js');
15
15
  var JSZip = require('jszip');
16
16
  var sha3 = require('@noble/hashes/sha3');
17
17
  var blake2b = require('@noble/hashes/blake2b');
@@ -26252,6 +26252,8 @@ var nested = {
26252
26252
  MessageType_DeviceInfo: 60601,
26253
26253
  MessageType_DeviceStatusGet: 60602,
26254
26254
  MessageType_DeviceStatus: 60603,
26255
+ MessageType_ResourceInventoryGet: 60604,
26256
+ MessageType_ResourceInventory: 60605,
26255
26257
  MessageType_FilesystemPermissionFix: 60800,
26256
26258
  MessageType_FilesystemPathInfo: 60801,
26257
26259
  MessageType_FilesystemPathInfoQuery: 60802,
@@ -37844,6 +37846,16 @@ var nested = {
37844
37846
  APP: 85
37845
37847
  }
37846
37848
  },
37849
+ ResourceBundleType: {
37850
+ values: {
37851
+ IMAGES: 0,
37852
+ ANIMATION: 1,
37853
+ WALLPAPER: 2,
37854
+ TRANSLATIONS: 3,
37855
+ ROOBERT: 4,
37856
+ NOTO: 5
37857
+ }
37858
+ },
37847
37859
  DeviceFirmwareImageInfo: {
37848
37860
  fields: {
37849
37861
  version: {
@@ -38041,6 +38053,38 @@ var nested = {
38041
38053
  }
38042
38054
  }
38043
38055
  },
38056
+ ResourceInventoryGet: {
38057
+ fields: {
38058
+ }
38059
+ },
38060
+ ResourceInventoryItem: {
38061
+ fields: {
38062
+ type: {
38063
+ rule: "required",
38064
+ type: "ResourceBundleType",
38065
+ id: 1
38066
+ },
38067
+ size: {
38068
+ rule: "required",
38069
+ type: "uint32",
38070
+ id: 2
38071
+ },
38072
+ header_hash: {
38073
+ rule: "required",
38074
+ type: "bytes",
38075
+ id: 3
38076
+ }
38077
+ }
38078
+ },
38079
+ ResourceInventory: {
38080
+ fields: {
38081
+ items: {
38082
+ rule: "repeated",
38083
+ type: "ResourceInventoryItem",
38084
+ id: 1
38085
+ }
38086
+ }
38087
+ },
38044
38088
  DeviceSessionErrorCode: {
38045
38089
  values: {
38046
38090
  DeviceSessionError_None: 0,
@@ -39407,6 +39451,168 @@ const findLatestRelease = (releases) => {
39407
39451
  return leastRelease;
39408
39452
  };
39409
39453
 
39454
+ const PROTOCOL_V2_RESOURCE_TYPES = [
39455
+ 'images',
39456
+ 'animation',
39457
+ 'wallpaper',
39458
+ 'translations',
39459
+ 'roobert',
39460
+ 'noto',
39461
+ ];
39462
+ const PROTOCOL_V2_RESOURCE_DEVICE_PATHS = {
39463
+ images: 'vol0:/bundles/images/images.okpkg',
39464
+ animation: 'vol0:/bundles/images/animation.okpkg',
39465
+ wallpaper: 'vol0:/bundles/images/wallpaper.okpkg',
39466
+ translations: 'vol0:/bundles/translations/translations.okpkg',
39467
+ roobert: 'vol0:/bundles/font/roobert.okpkg',
39468
+ noto: 'vol0:/bundles/font/noto.okpkg',
39469
+ };
39470
+ const RESOURCE_TYPE_SET = new Set(PROTOCOL_V2_RESOURCE_TYPES);
39471
+ const SHA256_HEX_LENGTH = 64;
39472
+ const SHA3_512_HEX_LENGTH = 128;
39473
+ const PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS = 5 * 1000;
39474
+ const RESOURCE_TYPE_BY_DEVICE_VALUE = {
39475
+ '0': 'images',
39476
+ IMAGES: 'images',
39477
+ '1': 'animation',
39478
+ ANIMATION: 'animation',
39479
+ '2': 'wallpaper',
39480
+ WALLPAPER: 'wallpaper',
39481
+ '3': 'translations',
39482
+ TRANSLATIONS: 'translations',
39483
+ '4': 'roobert',
39484
+ ROOBERT: 'roobert',
39485
+ '5': 'noto',
39486
+ NOTO: 'noto',
39487
+ };
39488
+ function parseProtocolV2ResourceInventory(value) {
39489
+ const items = value === null || value === void 0 ? void 0 : value.items;
39490
+ if (!Array.isArray(items)) {
39491
+ throw new Error('Invalid Pro2 resource inventory: items must be an array');
39492
+ }
39493
+ const inventory = items.map((item, index) => {
39494
+ if (!item || typeof item !== 'object') {
39495
+ throw new Error(`Invalid Pro2 resource inventory item at ${index}`);
39496
+ }
39497
+ const raw = item;
39498
+ const type = RESOURCE_TYPE_BY_DEVICE_VALUE[String(raw.type).toUpperCase()];
39499
+ if (!type) {
39500
+ throw new Error(`Invalid Pro2 resource inventory type at ${index}`);
39501
+ }
39502
+ if (!Number.isSafeInteger(raw.size) || Number(raw.size) <= 0) {
39503
+ throw new Error(`Invalid Pro2 resource inventory size at ${index}`);
39504
+ }
39505
+ return {
39506
+ type,
39507
+ size: Number(raw.size),
39508
+ headerHash: normalizeHex$1(raw.header_hash, SHA3_512_HEX_LENGTH, 'inventory headerHash'),
39509
+ };
39510
+ });
39511
+ if (new Set(inventory.map(item => item.type)).size !== inventory.length) {
39512
+ throw new Error('Invalid Pro2 resource inventory: duplicate resource type');
39513
+ }
39514
+ return PROTOCOL_V2_RESOURCE_TYPES.flatMap(type => {
39515
+ const item = inventory.find(candidate => candidate.type === type);
39516
+ return item ? [item] : [];
39517
+ });
39518
+ }
39519
+ function requestProtocolV2ResourceInventory({ commands, timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS, }) {
39520
+ return __awaiter(this, void 0, void 0, function* () {
39521
+ const { message } = yield commands.typedCall('ResourceInventoryGet', 'ResourceInventory', {}, { timeoutMs });
39522
+ return parseProtocolV2ResourceInventory(message);
39523
+ });
39524
+ }
39525
+ function normalizeHex$1(value, expectedLength, field) {
39526
+ if (typeof value !== 'string') {
39527
+ throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
39528
+ }
39529
+ const normalized = value.replace(/^0x/i, '').toLowerCase();
39530
+ if (normalized.length !== expectedLength || !/^[0-9a-f]+$/.test(normalized)) {
39531
+ throw new Error(`Invalid Pro2 resource ${field}: expected ${expectedLength} hexadecimal characters`);
39532
+ }
39533
+ return normalized;
39534
+ }
39535
+ function validateResource(value, index) {
39536
+ if (!value || typeof value !== 'object') {
39537
+ throw new Error(`Invalid Pro2 resource at stable[${index}]`);
39538
+ }
39539
+ const resource = value;
39540
+ if (typeof resource.type !== 'string' || !RESOURCE_TYPE_SET.has(resource.type)) {
39541
+ throw new Error(`Invalid Pro2 resource type at stable[${index}]`);
39542
+ }
39543
+ if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
39544
+ throw new Error(`Invalid Pro2 resource url at stable[${index}]`);
39545
+ }
39546
+ if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
39547
+ throw new Error(`Invalid Pro2 resource size at stable[${index}]`);
39548
+ }
39549
+ return {
39550
+ type: resource.type,
39551
+ url: resource.url,
39552
+ size: Number(resource.size),
39553
+ fileHash: normalizeHex$1(resource.fileHash, SHA256_HEX_LENGTH, 'fileHash'),
39554
+ headerHash: normalizeHex$1(resource.headerHash, SHA3_512_HEX_LENGTH, 'headerHash'),
39555
+ };
39556
+ }
39557
+ function parseProtocolV2Resources(value) {
39558
+ if (value === undefined)
39559
+ return undefined;
39560
+ if (!value ||
39561
+ typeof value !== 'object' ||
39562
+ !Array.isArray(value.stable)) {
39563
+ throw new Error('Invalid Pro2 resources config: stable must be an array');
39564
+ }
39565
+ const stable = value.stable.map(validateResource);
39566
+ const types = new Set(stable.map(resource => resource.type));
39567
+ if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
39568
+ throw new Error('Invalid Pro2 resources config: stable must contain six unique resource types');
39569
+ }
39570
+ for (const type of PROTOCOL_V2_RESOURCE_TYPES) {
39571
+ if (!types.has(type)) {
39572
+ throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
39573
+ }
39574
+ }
39575
+ return {
39576
+ stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
39577
+ const resource = stable.find(item => item.type === type);
39578
+ if (!resource) {
39579
+ throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
39580
+ }
39581
+ return resource;
39582
+ }),
39583
+ };
39584
+ }
39585
+ function buildProtocolV2ResourceUpdatePlan({ resources, inventory, mode, forced = false, }) {
39586
+ if (mode === 'bootloader-recovery' || forced) {
39587
+ return {
39588
+ status: resources.length > 0 ? 'outdated' : 'valid',
39589
+ resources: [...resources],
39590
+ };
39591
+ }
39592
+ if (!inventory) {
39593
+ return { status: 'unknown', resources: [] };
39594
+ }
39595
+ const inventoryByType = new Map(inventory.map(item => [item.type, item]));
39596
+ const changedResources = resources.filter(resource => {
39597
+ const current = inventoryByType.get(resource.type);
39598
+ return (!current ||
39599
+ current.size !== resource.size ||
39600
+ current.headerHash.toLowerCase() !== resource.headerHash.toLowerCase());
39601
+ });
39602
+ return {
39603
+ status: changedResources.length === 0 ? 'valid' : 'outdated',
39604
+ resources: changedResources,
39605
+ };
39606
+ }
39607
+ function bytesToHex$3(bytes) {
39608
+ return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
39609
+ }
39610
+ function isProtocolV2ResourceFileValid(binary, resource) {
39611
+ if (binary.byteLength !== resource.size)
39612
+ return false;
39613
+ return bytesToHex$3(sha256.sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
39614
+ }
39615
+
39410
39616
  var _a$1;
39411
39617
  const Log$i = getLogger(exports.LoggerNames.Core);
39412
39618
  const FIRMWARE_FIELDS = [
@@ -39460,10 +39666,11 @@ class DataManager {
39460
39666
  return enrichedData;
39461
39667
  }
39462
39668
  static load(settings) {
39669
+ var _b;
39463
39670
  return __awaiter(this, void 0, void 0, function* () {
39464
39671
  this.settings = settings;
39465
39672
  if (!settings.fetchConfig) {
39466
- return;
39673
+ return false;
39467
39674
  }
39468
39675
  const url = settings.preRelease
39469
39676
  ? 'https://data.onekey.so/pre-config.json'
@@ -39502,6 +39709,7 @@ class DataManager {
39502
39709
  }
39503
39710
  }
39504
39711
  if (data) {
39712
+ const pro2Resources = parseProtocolV2Resources((_b = data.pro2) === null || _b === void 0 ? void 0 : _b.resources);
39505
39713
  Log$i.log(`[DataConfig] Config loaded successfully via [${fetchMethod}]`);
39506
39714
  this.deviceMap = {
39507
39715
  [hdShared.EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
@@ -39510,15 +39718,15 @@ class DataManager {
39510
39718
  [hdShared.EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
39511
39719
  [hdShared.EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
39512
39720
  [hdShared.EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
39513
- [hdShared.EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
39721
+ [hdShared.EDeviceType.Pro2]: Object.assign(Object.assign({}, this.enrichFirmwareReleaseInfo(data.pro2)), (pro2Resources ? { resources: pro2Resources } : undefined)),
39514
39722
  };
39515
39723
  this.assets = {
39516
39724
  bridge: data.bridge,
39517
39725
  };
39726
+ return true;
39518
39727
  }
39519
- else {
39520
- Log$i.warn('[DataConfig] All fetch methods failed, using built-in default config');
39521
- }
39728
+ Log$i.warn('[DataConfig] All fetch methods failed, using built-in default config');
39729
+ return false;
39522
39730
  });
39523
39731
  }
39524
39732
  static updateEnv(newEnv) {
@@ -39531,12 +39739,29 @@ class DataManager {
39531
39739
  static checkAndReloadData() {
39532
39740
  return __awaiter(this, void 0, void 0, function* () {
39533
39741
  if (getTimeStamp() - this.lastCheckTimestamp > 1000 * 60 * 60 * 3) {
39534
- yield this.load(this.settings).then(() => {
39742
+ const loaded = yield this.load(this.settings);
39743
+ if (loaded) {
39535
39744
  this.lastCheckTimestamp = getTimeStamp();
39536
- });
39745
+ }
39537
39746
  }
39538
39747
  });
39539
39748
  }
39749
+ static forceReloadData() {
39750
+ return __awaiter(this, void 0, void 0, function* () {
39751
+ if (!this.settings) {
39752
+ throw new Error('Remote config settings are not initialized');
39753
+ }
39754
+ const loaded = yield this.load(this.settings);
39755
+ if (!loaded) {
39756
+ throw new Error('Unable to refresh the latest remote config');
39757
+ }
39758
+ this.lastCheckTimestamp = getTimeStamp();
39759
+ });
39760
+ }
39761
+ static getProtocolV2Resources() {
39762
+ var _b, _c;
39763
+ return (_c = (_b = this.deviceMap[hdShared.EDeviceType.Pro2]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.stable;
39764
+ }
39540
39765
  static getProtobufMessages(schema = 'v1CurrentSchema') {
39541
39766
  return this.messages[schema];
39542
39767
  }
@@ -45840,7 +46065,6 @@ function buildComponentRelease({ configKey, component, currentVersions, currentV
45840
46065
  };
45841
46066
  }
45842
46067
  function buildProtocolV2FirmwareRelease({ currentVersions, currentVerification = {}, remotePayloadHashes = {}, firmwareType, release, }) {
45843
- var _a;
45844
46068
  if (!release) {
45845
46069
  return {
45846
46070
  firmwareType,
@@ -45874,9 +46098,6 @@ function buildProtocolV2FirmwareRelease({ currentVersions, currentVerification =
45874
46098
  }
45875
46099
  }
45876
46100
  const targetsToUpdate = components.flatMap(component => component.status === 'outdated' && component.updateTarget ? [component.updateTarget] : []);
45877
- if (targetsToUpdate.length > 0 && ((_a = release.resourceBundles) === null || _a === void 0 ? void 0 : _a.length)) {
45878
- targetsToUpdate.push('resource');
45879
- }
45880
46101
  const uniqueTargetsToUpdate = Array.from(new Set(targetsToUpdate));
45881
46102
  const hasUpgrade = uniqueTargetsToUpdate.length > 0;
45882
46103
  const required = release.required && hasUpgrade;
@@ -45981,9 +46202,39 @@ class CheckAllFirmwareRelease extends BaseMethod {
45981
46202
  firmwareType,
45982
46203
  release,
45983
46204
  });
46205
+ const resources = DataManager.getProtocolV2Resources();
46206
+ let resourceStatus = 'unknown';
46207
+ if (resources === null || resources === void 0 ? void 0 : resources.length) {
46208
+ const loaderMode = state.status.mode === 'bootloader' || state.status.mode === 'romloader';
46209
+ if (loaderMode) {
46210
+ resourceStatus = buildProtocolV2ResourceUpdatePlan({
46211
+ resources,
46212
+ mode: 'bootloader-recovery',
46213
+ }).status;
46214
+ }
46215
+ else if (state.status.mode === 'normal') {
46216
+ try {
46217
+ const inventory = yield requestProtocolV2ResourceInventory({
46218
+ commands: this.device.getCommands(),
46219
+ });
46220
+ resourceStatus = buildProtocolV2ResourceUpdatePlan({
46221
+ resources,
46222
+ inventory,
46223
+ mode: 'application',
46224
+ }).status;
46225
+ }
46226
+ catch (_b) {
46227
+ resourceStatus = 'unknown';
46228
+ }
46229
+ }
46230
+ }
46231
+ const targetsToUpdate = [
46232
+ ...plan.targetsToUpdate,
46233
+ ...(resourceStatus === 'outdated' ? ['resource'] : []),
46234
+ ];
45984
46235
  const firmwareStatus = plan.status === 'unavailable' ? 'unknown' : plan.status;
45985
46236
  const emptyRelease = 'none';
45986
- return Object.assign({ firmware: {
46237
+ return Object.assign(Object.assign({ firmware: {
45987
46238
  status: firmwareStatus,
45988
46239
  changelog: release ? [release.changelog] : [],
45989
46240
  release: release !== null && release !== void 0 ? release : emptyRelease,
@@ -45994,7 +46245,7 @@ class CheckAllFirmwareRelease extends BaseMethod {
45994
46245
  }, bootloader: {
45995
46246
  status: 'valid',
45996
46247
  release: emptyRelease,
45997
- }, features, protocol: 'V2', deviceType: 'pro2' }, plan);
46248
+ }, features, protocol: 'V2', deviceType: 'pro2' }, plan), { resourceStatus, hasUpgrade: plan.hasUpgrade || resourceStatus === 'outdated', targetsToUpdate });
45998
46249
  });
45999
46250
  }
46000
46251
  }
@@ -48855,18 +49106,6 @@ const normalizeProtocolV2TargetStatus = (status) => {
48855
49106
  return undefined;
48856
49107
  };
48857
49108
  const normalizeProtocolV2Hex = (value) => value === null || value === void 0 ? void 0 : value.replace(/^0x/i, '').toLowerCase();
48858
- const versionArrayToNumber = (version) => {
48859
- if (!version)
48860
- return undefined;
48861
- return version[0] * 0x10000 + version[1] * 0x100 + version[2];
48862
- };
48863
- const compareProtocolV2Versions = (current, target) => {
48864
- const currentNumber = versionArrayToNumber(current);
48865
- const targetNumber = versionArrayToNumber(target);
48866
- if (currentNumber === undefined || targetNumber === undefined)
48867
- return undefined;
48868
- return currentNumber - targetNumber;
48869
- };
48870
49109
  const bytesToHex = (bytes) => Array.from(bytes)
48871
49110
  .map(byte => byte.toString(16).padStart(2, '0'))
48872
49111
  .join('');
@@ -49005,17 +49244,14 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
49005
49244
  platform: payload.platform,
49006
49245
  };
49007
49246
  }
49008
- getProtocolV2FirmwareChunkSize(direction = 'write') {
49247
+ getProtocolV2FirmwareChunkSize() {
49009
49248
  var _a, _b;
49010
49249
  const payloadChunkSize = Number((_a = this.params) === null || _a === void 0 ? void 0 : _a.chunkSize);
49011
49250
  const env = DataManager.getSettings('env');
49012
49251
  const isBle = ((_b = this.params) === null || _b === void 0 ? void 0 : _b.platform) === 'native' || (env && DataManager.isBleConnect(env));
49013
49252
  let maxChunkSize = hdTransport.PROTOCOL_V2_WEBUSB_FILE_CHUNK_SIZE;
49014
49253
  if (isBle) {
49015
- maxChunkSize =
49016
- direction === 'read'
49017
- ? hdTransport.PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE
49018
- : hdTransport.PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
49254
+ maxChunkSize = hdTransport.PROTOCOL_V2_BLE_FILE_CHUNK_SIZE;
49019
49255
  }
49020
49256
  if (!Number.isFinite(payloadChunkSize) || payloadChunkSize <= 0) {
49021
49257
  return maxChunkSize;
@@ -49029,12 +49265,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
49029
49265
  });
49030
49266
  }
49031
49267
  runProtocolV2() {
49032
- var _a, _b;
49268
+ var _a, _b, _c, _d;
49033
49269
  return __awaiter(this, void 0, void 0, function* () {
49034
49270
  yield this.captureProtocolV2PhysicalIdentity();
49035
49271
  const deviceFeatures = yield this.getProtocolV2DeviceFeatures();
49036
49272
  const deviceFirmwareType = getFirmwareType(deviceFeatures);
49037
49273
  const firmwareType = (_a = this.params.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
49274
+ const resourceRecoveryMode = Boolean(this.isProtocolV2BootloaderMode() || this.isProtocolV2RomloaderMode());
49038
49275
  let fwBinaryMap = [];
49039
49276
  let bootloaderBinary = null;
49040
49277
  let installItems;
@@ -49043,22 +49280,29 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
49043
49280
  this.postTipMessage(exports.FirmwareUpdateTipMessage.StartDownloadFirmware);
49044
49281
  fwBinaryMap = this.collectExplicitTargetBinaries();
49045
49282
  bootloaderBinary = this.prepareBootloaderBinary();
49046
- if (!this.hasExplicitProtocolV2Payload(fwBinaryMap)) {
49283
+ const needsRemoteFirmware = !this.hasExplicitProtocolV2Payload(fwBinaryMap);
49284
+ const needsRemoteResources = !((_b = this.params.resourceBundleFiles) === null || _b === void 0 ? void 0 : _b.length) &&
49285
+ !!((_c = this.params.targetsToUpdate) === null || _c === void 0 ? void 0 : _c.includes('resource'));
49286
+ if (needsRemoteFirmware || needsRemoteResources) {
49287
+ yield DataManager.forceReloadData();
49288
+ }
49289
+ if (needsRemoteFirmware) {
49047
49290
  const remoteBinaries = yield this.prepareRemoteProtocolV2Binaries(firmwareType, deviceFeatures);
49048
49291
  bootloaderBinary = remoteBinaries.bootloaderBinary;
49049
49292
  fwBinaryMap = remoteBinaries.fwBinaryMap;
49050
49293
  installItems = remoteBinaries.installItems;
49051
49294
  }
49052
- resourceBundles = this.prepareProtocolV2ResourceBundles(firmwareType, deviceFeatures);
49053
- if (resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length) {
49054
- resourceBundles = yield this.prefetchProtocolV2ResourceBundles(resourceBundles);
49055
- }
49295
+ resourceBundles = yield this.prepareProtocolV2ResourceBundles(resourceRecoveryMode);
49056
49296
  this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
49057
49297
  }
49058
49298
  catch (err) {
49059
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, (_b = err.message) !== null && _b !== void 0 ? _b : err);
49299
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, (_d = err.message) !== null && _d !== void 0 ? _d : err);
49060
49300
  }
49061
49301
  if (!bootloaderBinary && fwBinaryMap.length === 0 && !(resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length)) {
49302
+ if (resourceBundles !== undefined) {
49303
+ this.postTipMessage(exports.FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
49304
+ return this.getProtocolV2VersionResult(deviceFeatures);
49305
+ }
49062
49306
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, 'No firmware to update');
49063
49307
  }
49064
49308
  yield this.enterProtocolV2BootloaderMode();
@@ -49166,57 +49410,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
49166
49410
  }
49167
49411
  return target;
49168
49412
  }
49169
- getProtocolV2ResourceFilePath(path) {
49170
- if (path.startsWith('vol'))
49171
- return path;
49172
- if (path.startsWith('/'))
49173
- return `vol0:${path}`;
49174
- return `vol0:/${path}`;
49175
- }
49176
- readProtocolV2DeviceFileHeader(path) {
49177
- var _a, _b, _c, _d;
49178
- return __awaiter(this, void 0, void 0, function* () {
49179
- const typedCall = this.device.getCommands().typedCall.bind(this.device.getCommands());
49180
- const filePath = this.getProtocolV2ResourceFilePath(path);
49181
- const pathInfoRes = yield typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', {
49182
- path: filePath,
49183
- });
49184
- const fileSize = toProtocolV2FiniteNumber((_a = pathInfoRes.message) === null || _a === void 0 ? void 0 : _a.size);
49185
- if (!((_b = pathInfoRes.message) === null || _b === void 0 ? void 0 : _b.exist) ||
49186
- ((_c = pathInfoRes.message) === null || _c === void 0 ? void 0 : _c.directory) ||
49187
- fileSize === undefined ||
49188
- fileSize < PROTOCOL_V2_OKPP_HEADER_SIZE) {
49189
- return null;
49190
- }
49191
- const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
49192
- const chunks = [];
49193
- let offset = 0;
49194
- while (offset < PROTOCOL_V2_OKPP_HEADER_SIZE) {
49195
- const readLen = Math.min(chunkSize, PROTOCOL_V2_OKPP_HEADER_SIZE - offset);
49196
- const res = yield typedCall('FilesystemFileRead', 'FilesystemFile', {
49197
- file: {
49198
- path: filePath,
49199
- offset,
49200
- total_size: 0,
49201
- },
49202
- chunk_len: readLen,
49203
- ui_percentage: undefined,
49204
- });
49205
- const data = toProtocolV2Bytes((_d = res.message) === null || _d === void 0 ? void 0 : _d.data);
49206
- if (data.byteLength === 0)
49207
- return null;
49208
- chunks.push(data);
49209
- offset += data.byteLength;
49210
- }
49211
- const headerBytes = new Uint8Array(offset);
49212
- let cursor = 0;
49213
- chunks.forEach(chunk => {
49214
- headerBytes.set(chunk, cursor);
49215
- cursor += chunk.byteLength;
49216
- });
49217
- return parseProtocolV2OkppHeader(headerBytes);
49218
- });
49219
- }
49220
49413
  downloadRemoteProtocolV2Component(key, component) {
49221
49414
  return __awaiter(this, void 0, void 0, function* () {
49222
49415
  const target = this.getRemoteComponentTarget(key, component);
@@ -49288,103 +49481,71 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
49288
49481
  };
49289
49482
  });
49290
49483
  }
49291
- prepareProtocolV2ResourceBundles(firmwareType, features) {
49292
- var _a, _b, _c;
49293
- if ((_a = this.params.resourceBundleFiles) === null || _a === void 0 ? void 0 : _a.length) {
49294
- return this.params.resourceBundleFiles.map((file, index) => {
49295
- var _a;
49296
- const devicePath = validateProtocolV2FilesystemPath(file.devicePath, `resourceBundleFiles[${index}].devicePath`);
49297
- return {
49298
- name: (_a = devicePath.split('/').pop()) !== null && _a !== void 0 ? _a : devicePath,
49299
- binary: file.binary,
49300
- devicePath,
49301
- };
49484
+ prepareProtocolV2ResourceBundles(recoveryMode) {
49485
+ var _a, _b;
49486
+ return __awaiter(this, void 0, void 0, function* () {
49487
+ if ((_a = this.params.resourceBundleFiles) === null || _a === void 0 ? void 0 : _a.length) {
49488
+ return this.params.resourceBundleFiles.map((file, index) => {
49489
+ var _a;
49490
+ const devicePath = validateProtocolV2FilesystemPath(file.devicePath, `resourceBundleFiles[${index}].devicePath`);
49491
+ return {
49492
+ name: (_a = devicePath.split('/').pop()) !== null && _a !== void 0 ? _a : devicePath,
49493
+ binary: file.binary,
49494
+ devicePath,
49495
+ };
49496
+ });
49497
+ }
49498
+ if (!((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('resource'))) {
49499
+ return undefined;
49500
+ }
49501
+ const resources = DataManager.getProtocolV2Resources();
49502
+ if (!(resources === null || resources === void 0 ? void 0 : resources.length)) {
49503
+ throw new Error('Missing Pro2 stable resource configuration');
49504
+ }
49505
+ const inventory = recoveryMode
49506
+ ? undefined
49507
+ : yield requestProtocolV2ResourceInventory({
49508
+ commands: this.device.getCommands(),
49509
+ timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
49510
+ });
49511
+ const plan = buildProtocolV2ResourceUpdatePlan({
49512
+ resources,
49513
+ inventory,
49514
+ mode: recoveryMode ? 'bootloader-recovery' : 'application',
49515
+ forced: this.params.forcedUpdateRes,
49302
49516
  });
49303
- }
49304
- if (!((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('resource'))) {
49305
- return undefined;
49306
- }
49307
- const release = DataManager.getFirmwareLatestRelease(features, firmwareType);
49308
- if (!((_c = release === null || release === void 0 ? void 0 : release.resourceBundles) === null || _c === void 0 ? void 0 : _c.length))
49309
- return undefined;
49310
- return release.resourceBundles.map((bundle, index) => ({
49311
- name: bundle.name,
49312
- binary: new ArrayBuffer(0),
49313
- devicePath: validateProtocolV2FilesystemPath(bundle.devicePath, `resourceBundles[${index}].devicePath`),
49314
- url: bundle.url,
49315
- version: bundle.version,
49316
- payloadHash: bundle.payloadHash,
49317
- headerHash: bundle.headerHash,
49318
- }));
49517
+ Log$5.log(`[FirmwareUpdateV4] Pro2 resource plan mode=${recoveryMode ? 'bootloader-recovery' : 'application'} status=${plan.status} count=${plan.resources.length}`);
49518
+ const bundles = [];
49519
+ for (const resource of plan.resources) {
49520
+ bundles.push(yield this.downloadProtocolV2Resource(resource));
49521
+ }
49522
+ return bundles;
49523
+ });
49319
49524
  }
49320
- prefetchProtocolV2ResourceBundles(bundles) {
49525
+ downloadProtocolV2Resource(resource) {
49321
49526
  return __awaiter(this, void 0, void 0, function* () {
49322
- const prefetched = [];
49323
- for (const bundle of bundles) {
49324
- let { binary } = bundle;
49325
- let downloadedFromRemote = false;
49326
- if (binary.byteLength === 0) {
49327
- if (!bundle.url) {
49328
- throw new Error(`Missing Protocol V2 RESC bundle binary: ${bundle.name}`);
49329
- }
49330
- Log$5.log(`[FirmwareUpdateV4] downloading remote RESC bundle ${bundle.name}`);
49331
- ({ binary } = yield getSysResourceBinary(bundle.url));
49332
- downloadedFromRemote = true;
49333
- }
49334
- if (binary.byteLength === 0) {
49335
- throw new Error(`Protocol V2 RESC bundle is empty: ${bundle.name}`);
49336
- }
49337
- if (downloadedFromRemote) {
49338
- const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
49339
- if (!header || header.type !== 'RESC') {
49340
- throw new Error(`Invalid Protocol V2 RESC bundle header: ${bundle.name}`);
49341
- }
49342
- if (bundle.version && compareProtocolV2Versions(header.version, bundle.version) !== 0) {
49343
- throw new Error(`Protocol V2 RESC bundle version mismatch: ${bundle.name}`);
49344
- }
49345
- const expectedPayloadHash = normalizeProtocolV2Hex(bundle.payloadHash);
49346
- if (expectedPayloadHash && header.payloadHash !== expectedPayloadHash) {
49347
- throw new Error(`Protocol V2 RESC bundle payload hash mismatch: ${bundle.name}`);
49348
- }
49349
- const expectedHeaderHash = normalizeProtocolV2Hex(bundle.headerHash);
49350
- if (expectedHeaderHash && header.headerHash !== expectedHeaderHash) {
49351
- throw new Error(`Protocol V2 RESC bundle header hash mismatch: ${bundle.name}`);
49352
- }
49353
- }
49354
- prefetched.push(Object.assign(Object.assign({}, bundle), { binary }));
49527
+ Log$5.log(`[FirmwareUpdateV4] downloading Pro2 resource ${resource.type}`);
49528
+ const { binary } = yield getSysResourceBinary(resource.url);
49529
+ if (!isProtocolV2ResourceFileValid(binary, resource)) {
49530
+ throw new Error(`Pro2 resource file verification failed: ${resource.type}`);
49355
49531
  }
49356
- return prefetched;
49532
+ return {
49533
+ name: `${resource.type}.okpkg`,
49534
+ binary,
49535
+ devicePath: PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type],
49536
+ };
49357
49537
  });
49358
49538
  }
49359
49539
  syncProtocolV2ResourceBundles(bundles, firmwareSize) {
49360
49540
  return __awaiter(this, void 0, void 0, function* () {
49361
49541
  const transferStartTime = Date.now();
49362
49542
  const transferTransport = this.getProtocolV2FirmwareTransferTransport();
49363
- const isRemoteMode = bundles.some(bundle => !!bundle.url);
49364
- let bundlesToSync = bundles;
49365
- if (isRemoteMode) {
49366
- const filtered = [];
49367
- for (const bundle of bundles) {
49368
- const upToDate = yield this.isProtocolV2ResourceBundleUpToDate(bundle);
49369
- if (upToDate) {
49370
- Log$5.log(`[FirmwareUpdateV4] skip RESC bundle ${bundle.name}; already up to date`);
49371
- }
49372
- else {
49373
- filtered.push(bundle);
49374
- }
49375
- }
49376
- bundlesToSync = filtered;
49377
- }
49378
- if (bundlesToSync.length === 0) {
49379
- Log$5.log('[FirmwareUpdateV4] all RESC bundles up to date, nothing to sync');
49380
- return { processedSize: 0, totalSize: firmwareSize };
49381
- }
49382
49543
  let totalSize = 0;
49383
- for (const b of bundlesToSync)
49544
+ for (const b of bundles)
49384
49545
  totalSize += b.binary.byteLength;
49385
49546
  const transferTotalSize = totalSize + firmwareSize;
49386
49547
  let processedSize = 0;
49387
- for (const bundle of bundlesToSync) {
49548
+ for (const bundle of bundles) {
49388
49549
  Log$5.log(`[FirmwareUpdateV4] syncing RESC bundle ${bundle.name} -> ${bundle.devicePath} bytes=${bundle.binary.byteLength}`);
49389
49550
  processedSize = yield this.protocolV2CommonUpdateProcess({
49390
49551
  payload: bundle.binary,
@@ -49398,43 +49559,10 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
49398
49559
  return { processedSize, totalSize: transferTotalSize };
49399
49560
  });
49400
49561
  }
49401
- isProtocolV2ResourceBundleUpToDate(bundle) {
49402
- return __awaiter(this, void 0, void 0, function* () {
49403
- if (this.params.forcedUpdateRes)
49404
- return false;
49405
- if (!bundle.version && !bundle.payloadHash)
49406
- return false;
49407
- try {
49408
- const header = yield this.readProtocolV2DeviceFileHeader(bundle.devicePath);
49409
- if (!header)
49410
- return false;
49411
- if (bundle.version) {
49412
- const cmp = compareProtocolV2Versions(header.version, bundle.version);
49413
- if (cmp === undefined || cmp !== 0)
49414
- return false;
49415
- }
49416
- if (bundle.payloadHash) {
49417
- const expected = normalizeProtocolV2Hex(bundle.payloadHash);
49418
- if (expected && header.payloadHash !== expected)
49419
- return false;
49420
- }
49421
- if (bundle.headerHash) {
49422
- const expected = normalizeProtocolV2Hex(bundle.headerHash);
49423
- if (expected && header.headerHash !== expected)
49424
- return false;
49425
- }
49426
- return true;
49427
- }
49428
- catch (error) {
49429
- Log$5.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
49430
- return false;
49431
- }
49432
- });
49433
- }
49434
49562
  isProtocolV2BootloaderMode() {
49435
49563
  var _a, _b, _c;
49436
49564
  if (typeof this.device.isBootloader === 'function') {
49437
- return this.device.isBootloader();
49565
+ return !!this.device.isBootloader();
49438
49566
  }
49439
49567
  return (((_a = this.device.features) === null || _a === void 0 ? void 0 : _a.mode) === 'bootloader' ||
49440
49568
  (((_b = this.device.features) === null || _b === void 0 ? void 0 : _b.mode) == null && !!((_c = this.device.features) === null || _c === void 0 ? void 0 : _c.bootloaderMode)));
@@ -49719,19 +49847,22 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
49719
49847
  waitForProtocolV2FinalFeatures() {
49720
49848
  return __awaiter(this, void 0, void 0, function* () {
49721
49849
  const features = yield this.waitForProtocolV2ReconnectAndFeatures(PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT);
49722
- const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
49723
- const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
49724
- const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
49725
- if (firmwareVersion === '0.0.0') {
49726
- Log$5.warn('Protocol V2 firmware update finished but app firmware version is still 0.0.0. This is allowed for Pro2 debug BLE-only update flows.');
49727
- }
49728
- return {
49729
- bootloaderVersion,
49730
- bleVersion,
49731
- firmwareVersion,
49732
- };
49850
+ return this.getProtocolV2VersionResult(features);
49733
49851
  });
49734
49852
  }
49853
+ getProtocolV2VersionResult(features) {
49854
+ const bootloaderVersion = getDeviceBootloaderVersion(features).join('.');
49855
+ const bleVersion = getDeviceBLEFirmwareVersion(features).join('.');
49856
+ const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
49857
+ if (firmwareVersion === '0.0.0') {
49858
+ Log$5.warn('Protocol V2 firmware update finished but app firmware version is still 0.0.0. This is allowed for Pro2 debug BLE-only update flows.');
49859
+ }
49860
+ return {
49861
+ bootloaderVersion,
49862
+ bleVersion,
49863
+ firmwareVersion,
49864
+ };
49865
+ }
49735
49866
  waitForProtocolV2ReconnectAndFeatures(timeout) {
49736
49867
  return __awaiter(this, void 0, void 0, function* () {
49737
49868
  const startTime = Date.now();