@onekeyfe/hd-core 1.2.0-alpha.78 → 1.2.0-alpha.79

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 (38) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +12 -71
  2. package/__tests__/protocol-v2-resources.test.ts +102 -222
  3. package/__tests__/protocol-v2.test.ts +110 -380
  4. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  5. package/dist/api/FirmwareUpdateV4.d.ts +0 -6
  6. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  7. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  8. package/dist/data-manager/DataManager.d.ts +1 -2
  9. package/dist/data-manager/DataManager.d.ts.map +1 -1
  10. package/dist/index.d.ts +74 -32
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +255 -499
  13. package/dist/inject.d.ts.map +1 -1
  14. package/dist/protocols/protocol-v2/resources.d.ts +31 -28
  15. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  16. package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -0
  17. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  18. package/dist/types/api/export.d.ts +1 -0
  19. package/dist/types/api/export.d.ts.map +1 -1
  20. package/dist/types/api/index.d.ts +2 -0
  21. package/dist/types/api/index.d.ts.map +1 -1
  22. package/dist/types/api/protocolV2ResourceManifest.d.ts +17 -0
  23. package/dist/types/api/protocolV2ResourceManifest.d.ts.map +1 -0
  24. package/dist/types/settings.d.ts +30 -21
  25. package/dist/types/settings.d.ts.map +1 -1
  26. package/package.json +4 -4
  27. package/src/api/CheckAllFirmwareRelease.ts +5 -34
  28. package/src/api/FirmwareUpdateV4.ts +48 -220
  29. package/src/api/UploadPortfolio.ts +18 -0
  30. package/src/data-manager/DataManager.ts +2 -6
  31. package/src/index.ts +6 -0
  32. package/src/inject.ts +2 -0
  33. package/src/protocols/protocol-v2/resources.ts +193 -324
  34. package/src/types/api/checkAllFirmwareRelease.ts +1 -0
  35. package/src/types/api/export.ts +4 -0
  36. package/src/types/api/index.ts +2 -0
  37. package/src/types/api/protocolV2ResourceManifest.ts +19 -0
  38. package/src/types/settings.ts +30 -36
package/dist/index.js CHANGED
@@ -1265,6 +1265,204 @@ const assertFirmwareUpdatePreparedPlanBinding = ({ preparedPlan: value, executor
1265
1265
  return preparedPlan;
1266
1266
  };
1267
1267
 
1268
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1 = 'vol0:/loaders/bootloader/boot_resource.okpkg';
1269
+ const SHA256_HEX_LENGTH = 64;
1270
+ function normalizeHex$1(value, expectedLength, field) {
1271
+ if (typeof value !== 'string') {
1272
+ throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
1273
+ }
1274
+ const normalized = value.replace(/^0x/i, '').toLowerCase();
1275
+ if (normalized.length !== expectedLength || !/^[0-9a-f]+$/.test(normalized)) {
1276
+ throw new Error(`Invalid Pro2 resource ${field}: expected ${expectedLength} hexadecimal characters`);
1277
+ }
1278
+ return normalized;
1279
+ }
1280
+ function parseProtocolV2Resources(value) {
1281
+ if (value === undefined)
1282
+ return undefined;
1283
+ if (!value || typeof value !== 'object') {
1284
+ throw new Error('Invalid Pro2 resources config');
1285
+ }
1286
+ const { source } = value;
1287
+ if (!source || typeof source !== 'object') {
1288
+ throw new Error('Invalid Pro2 resources config: source is required');
1289
+ }
1290
+ const { manifestUrl } = source;
1291
+ if (typeof manifestUrl !== 'string' || !manifestUrl.startsWith('https://')) {
1292
+ throw new Error('Invalid Pro2 resources config: source.manifestUrl must use HTTPS');
1293
+ }
1294
+ return {
1295
+ source: { manifestUrl },
1296
+ };
1297
+ }
1298
+ const PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS = [
1299
+ 'vol0:/bundles/',
1300
+ 'vol0:/loaders/rom/',
1301
+ ];
1302
+ function isAllowedManifestDevicePath(path) {
1303
+ if (!path.endsWith('.okpkg') ||
1304
+ path.includes('\\') ||
1305
+ path.includes('//') ||
1306
+ path.split('/').some(part => part === '.' || part === '..')) {
1307
+ return false;
1308
+ }
1309
+ if (path === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1) {
1310
+ return true;
1311
+ }
1312
+ return PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS.some(root => path.startsWith(root));
1313
+ }
1314
+ function assertManifestString(value, field) {
1315
+ if (typeof value !== 'string' || value.length === 0) {
1316
+ throw new Error(`Invalid Pro2 resource manifest ${field}`);
1317
+ }
1318
+ return value;
1319
+ }
1320
+ function assertManifestRelativePath(value, field) {
1321
+ const path = assertManifestString(value, field);
1322
+ if (path.startsWith('/') ||
1323
+ path.includes('\\') ||
1324
+ path.includes(':') ||
1325
+ path.split('/').some(part => !part || part === '.' || part === '..')) {
1326
+ throw new Error(`Invalid Pro2 resource manifest ${field}`);
1327
+ }
1328
+ return path;
1329
+ }
1330
+ function parseProtocolV2ResourceManifestFile(value, index) {
1331
+ var _a;
1332
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
1333
+ throw new Error(`Invalid Pro2 resource manifest files[${index}]`);
1334
+ }
1335
+ const file = value;
1336
+ const archivePath = assertManifestRelativePath(file.archive_path, `files[${index}].archive_path`);
1337
+ const originalName = assertManifestRelativePath(file.original_name, `files[${index}].original_name`);
1338
+ if (originalName.includes('/')) {
1339
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].original_name`);
1340
+ }
1341
+ const devicePath = assertManifestString(file.device_path, `files[${index}].device_path`);
1342
+ if (!isAllowedManifestDevicePath(devicePath)) {
1343
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].device_path`);
1344
+ }
1345
+ if (!Number.isSafeInteger(file.size) || Number(file.size) <= 0) {
1346
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].size`);
1347
+ }
1348
+ const digest = normalizeHex$1(file.sha256, SHA256_HEX_LENGTH, `files[${index}].sha256`);
1349
+ if (file.signed !== true) {
1350
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].signed`);
1351
+ }
1352
+ if (file.sig_algo !== 'ed25519' && file.sig_algo !== 'mldsa65') {
1353
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].sig_algo`);
1354
+ }
1355
+ if (file.payload_version !== null && typeof file.payload_version !== 'string') {
1356
+ throw new Error(`Invalid Pro2 resource manifest files[${index}].payload_version`);
1357
+ }
1358
+ if (!archivePath.endsWith('.okpkg') || !originalName.endsWith('.okpkg')) {
1359
+ throw new Error(`Invalid Pro2 resource manifest files[${index}] package extension`);
1360
+ }
1361
+ return {
1362
+ archive_path: archivePath,
1363
+ original_name: originalName,
1364
+ device_path: devicePath,
1365
+ size: Number(file.size),
1366
+ sha256: digest,
1367
+ signed: true,
1368
+ sig_algo: file.sig_algo,
1369
+ payload_version: (_a = file.payload_version) !== null && _a !== void 0 ? _a : null,
1370
+ };
1371
+ }
1372
+ function parseProtocolV2ResourceManifest(value) {
1373
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
1374
+ throw new Error('Invalid Pro2 resource manifest');
1375
+ }
1376
+ const manifest = value;
1377
+ if (manifest.schema !== 1 ||
1378
+ manifest.variant !== 'resource' ||
1379
+ manifest.device_root !== 'vol0:' ||
1380
+ manifest.restore_mode !== 'bootloader_update' ||
1381
+ !Array.isArray(manifest.trees) ||
1382
+ !Array.isArray(manifest.files)) {
1383
+ throw new Error('Invalid Pro2 resource manifest contract');
1384
+ }
1385
+ const files = manifest.files.map(parseProtocolV2ResourceManifestFile);
1386
+ const devicePaths = new Set(files.map(file => file.device_path));
1387
+ const archivePaths = new Set(files.map(file => file.archive_path));
1388
+ if (files.length === 0 ||
1389
+ devicePaths.size !== files.length ||
1390
+ archivePaths.size !== files.length ||
1391
+ !devicePaths.has(PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH$1) ||
1392
+ !files.some(file => file.device_path.startsWith('vol0:/bundles/'))) {
1393
+ throw new Error('Invalid Pro2 resource manifest file set');
1394
+ }
1395
+ return {
1396
+ schema: 1,
1397
+ artifact_name: assertManifestString(manifest.artifact_name, 'artifact_name'),
1398
+ release_name: assertManifestString(manifest.release_name, 'release_name'),
1399
+ variant: 'resource',
1400
+ commit: assertManifestString(manifest.commit, 'commit'),
1401
+ short_sha: assertManifestString(manifest.short_sha, 'short_sha'),
1402
+ timestamp_utc: assertManifestString(manifest.timestamp_utc, 'timestamp_utc'),
1403
+ core_version: assertManifestString(manifest.core_version, 'core_version'),
1404
+ key_set: assertManifestString(manifest.key_set, 'key_set'),
1405
+ device_root: 'vol0:',
1406
+ restore_mode: 'bootloader_update',
1407
+ trees: manifest.trees.map((tree, index) => {
1408
+ if (!tree || typeof tree !== 'object') {
1409
+ throw new Error(`Invalid Pro2 resource manifest trees[${index}]`);
1410
+ }
1411
+ const item = tree;
1412
+ return {
1413
+ path: assertManifestRelativePath(item.path, `trees[${index}].path`),
1414
+ device: assertManifestString(item.device, `trees[${index}].device`),
1415
+ };
1416
+ }),
1417
+ files,
1418
+ };
1419
+ }
1420
+ function selectProtocolV2ResourceManifestFiles({ manifest, targetsToUpdate, }) {
1421
+ const targets = new Set(targetsToUpdate);
1422
+ return manifest.files.filter(file => {
1423
+ if (file.device_path.startsWith('vol0:/bundles/')) {
1424
+ return targets.has('resource');
1425
+ }
1426
+ return targets.has('boot_resources');
1427
+ });
1428
+ }
1429
+ function resolveProtocolV2ResourceManifestFileUrl({ manifestUrl, archivePath, }) {
1430
+ const url = new URL(assertManifestRelativePath(archivePath, 'archive_path'), manifestUrl);
1431
+ if (url.protocol !== 'https:') {
1432
+ throw new Error('Invalid Pro2 resource manifest file URL');
1433
+ }
1434
+ return url.toString();
1435
+ }
1436
+ function prepareProtocolV2ResourceFiles({ manifest: value, files, targetsToUpdate, }) {
1437
+ const manifest = parseProtocolV2ResourceManifest(value);
1438
+ const selected = selectProtocolV2ResourceManifestFiles({ manifest, targetsToUpdate });
1439
+ const binaries = new Map(files.map(file => [file.archivePath, file.binary]));
1440
+ return selected.map(file => {
1441
+ const binary = binaries.get(file.archive_path);
1442
+ if (!binary ||
1443
+ !isProtocolV2ResourceFileValid(binary, {
1444
+ size: file.size,
1445
+ fileHash: file.sha256,
1446
+ })) {
1447
+ throw new Error(`Pro2 resource manifest file verification failed: ${file.archive_path}`);
1448
+ }
1449
+ return {
1450
+ binary,
1451
+ devicePath: file.device_path,
1452
+ size: file.size,
1453
+ fileHash: file.sha256,
1454
+ };
1455
+ });
1456
+ }
1457
+ function bytesToHex$3(bytes) {
1458
+ return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
1459
+ }
1460
+ function isProtocolV2ResourceFileValid(binary, resource) {
1461
+ if (binary.byteLength !== resource.size)
1462
+ return false;
1463
+ return bytesToHex$3(sha256.sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
1464
+ }
1465
+
1268
1466
  const bindingError = (message) => {
1269
1467
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, message, {
1270
1468
  firmwareUpdateCode: 'FirmwareArtifactReaderInvalid',
@@ -1414,6 +1612,7 @@ const createCoreApi = (call) => ({
1414
1612
  getFirmwareUpdateHostBindingGeneration,
1415
1613
  prepareFirmwareUpdatePlan,
1416
1614
  validateFirmwareUpdatePreparedPlan,
1615
+ prepareProtocolV2ResourceFiles,
1417
1616
  getLogs: () => call({ method: 'getLogs' }),
1418
1617
  clearSessionCache: params => call(Object.assign(Object.assign({}, params), { method: 'clearSessionCache' })),
1419
1618
  searchDevices: params => call(Object.assign(Object.assign({}, params), { method: 'searchDevices' })),
@@ -40504,281 +40703,6 @@ const findLatestRelease = (releases) => {
40504
40703
  return leastRelease;
40505
40704
  };
40506
40705
 
40507
- const PROTOCOL_V2_RESOURCE_TYPES = [
40508
- 'images',
40509
- 'animation',
40510
- 'wallpaper',
40511
- 'translations',
40512
- 'roobert',
40513
- 'noto',
40514
- 'firmware_logo',
40515
- ];
40516
- const PROTOCOL_V2_RESOURCE_DEVICE_PATHS = {
40517
- images: 'vol0:/bundles/images/images.okpkg',
40518
- animation: 'vol0:/bundles/images/animation.okpkg',
40519
- wallpaper: 'vol0:/bundles/images/wallpaper.okpkg',
40520
- translations: 'vol0:/bundles/translations/translations.okpkg',
40521
- roobert: 'vol0:/bundles/font/roobert.okpkg',
40522
- noto: 'vol0:/bundles/font/noto.okpkg',
40523
- firmware_logo: 'vol0:/bundles/firmware_logo.okpkg',
40524
- };
40525
- const RESOURCE_TYPE_SET = new Set(PROTOCOL_V2_RESOURCE_TYPES);
40526
- const SHA256_HEX_LENGTH = 64;
40527
- const SHA3_512_HEX_LENGTH = 128;
40528
- const PROTOCOL_V2_OKPP_HEADER_SIZE$1 = 0x52a0;
40529
- const PROTOCOL_V2_OKPP_TYPE_OFFSET = 0x08;
40530
- const PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET = 0x0c;
40531
- const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 = 0x240;
40532
- const PROTOCOL_V2_OKPP_HASH_SIZE$1 = 64;
40533
- const PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE = PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 + PROTOCOL_V2_OKPP_HASH_SIZE$1;
40534
- const PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE = 64;
40535
- const PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS = 5 * 1000;
40536
- function toFiniteNumber(value) {
40537
- if (typeof value === 'number' && Number.isFinite(value))
40538
- return value;
40539
- if (typeof value === 'string') {
40540
- const numeric = Number(value);
40541
- return Number.isFinite(numeric) ? numeric : undefined;
40542
- }
40543
- if (value && typeof value === 'object') {
40544
- const longLike = value;
40545
- if (typeof longLike.toNumber === 'function') {
40546
- const numeric = longLike.toNumber();
40547
- return Number.isFinite(numeric) ? numeric : undefined;
40548
- }
40549
- }
40550
- return undefined;
40551
- }
40552
- function toUint8Array(value) {
40553
- if (value instanceof Uint8Array)
40554
- return value;
40555
- if (value instanceof ArrayBuffer)
40556
- return new Uint8Array(value);
40557
- if (ArrayBuffer.isView(value)) {
40558
- return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
40559
- }
40560
- if (typeof value === 'string') {
40561
- const hex = value.replace(/^0x/i, '');
40562
- if (!hex || hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
40563
- return new Uint8Array(0);
40564
- }
40565
- const bytes = new Uint8Array(hex.length / 2);
40566
- for (let index = 0; index < bytes.length; index += 1) {
40567
- bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
40568
- }
40569
- return bytes;
40570
- }
40571
- return new Uint8Array(0);
40572
- }
40573
- function readAscii(bytes, offset, length) {
40574
- return Array.from(bytes.slice(offset, offset + length), byte => String.fromCharCode(byte)).join('');
40575
- }
40576
- function parseProtocolV2ResourceHeaderHash(bytes) {
40577
- if (bytes.byteLength < PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE)
40578
- return undefined;
40579
- if (readAscii(bytes, 0, 4) !== 'OKPP')
40580
- return undefined;
40581
- if (readAscii(bytes, PROTOCOL_V2_OKPP_TYPE_OFFSET, 4) !== 'RESC')
40582
- return undefined;
40583
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
40584
- if (view.getUint32(PROTOCOL_V2_OKPP_HEADER_LENGTH_OFFSET, true) !== PROTOCOL_V2_OKPP_HEADER_SIZE$1) {
40585
- return undefined;
40586
- }
40587
- return bytesToHex$3(bytes.slice(PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1, PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET$1 + PROTOCOL_V2_OKPP_HASH_SIZE$1));
40588
- }
40589
- function readProtocolV2ResourceIdentity({ commands, resource, chunkSize, timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS, }) {
40590
- var _a, _b, _c, _d;
40591
- return __awaiter(this, void 0, void 0, function* () {
40592
- const path = PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type];
40593
- const pathInfo = yield commands.typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path }, { timeoutMs });
40594
- const size = toFiniteNumber((_a = pathInfo.message) === null || _a === void 0 ? void 0 : _a.size);
40595
- if (!((_b = pathInfo.message) === null || _b === void 0 ? void 0 : _b.exist) ||
40596
- ((_c = pathInfo.message) === null || _c === void 0 ? void 0 : _c.directory) ||
40597
- !Number.isSafeInteger(size) ||
40598
- size !== resource.size ||
40599
- size < PROTOCOL_V2_OKPP_HEADER_SIZE$1) {
40600
- return undefined;
40601
- }
40602
- const header = new Uint8Array(PROTOCOL_V2_RESOURCE_IDENTITY_READ_SIZE);
40603
- let offset = 0;
40604
- while (offset < header.byteLength) {
40605
- const readLength = Math.min(chunkSize, header.byteLength - offset);
40606
- const response = yield commands.typedCall('FilesystemFileRead', 'FilesystemFile', {
40607
- file: { path, offset, total_size: 0 },
40608
- chunk_len: readLength,
40609
- }, { timeoutMs });
40610
- const data = toUint8Array((_d = response.message) === null || _d === void 0 ? void 0 : _d.data);
40611
- if (data.byteLength === 0)
40612
- return undefined;
40613
- const copied = Math.min(data.byteLength, header.byteLength - offset);
40614
- header.set(data.subarray(0, copied), offset);
40615
- offset += copied;
40616
- }
40617
- const headerHash = parseProtocolV2ResourceHeaderHash(header);
40618
- return headerHash ? { type: resource.type, size, headerHash } : undefined;
40619
- });
40620
- }
40621
- function readProtocolV2ResourceInventory({ commands, resources, chunkSize = hdTransport.PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE, timeoutMs = PROTOCOL_V2_RESOURCE_INVENTORY_TIMEOUT_MS, }) {
40622
- return __awaiter(this, void 0, void 0, function* () {
40623
- const normalizedChunkSize = Number.isFinite(chunkSize)
40624
- ? Math.max(Math.floor(chunkSize), PROTOCOL_V2_MIN_FILE_READ_CHUNK_SIZE)
40625
- : hdTransport.PROTOCOL_V2_BLE_FILE_READ_CHUNK_SIZE;
40626
- const inventory = [];
40627
- for (const resource of resources) {
40628
- try {
40629
- const item = yield readProtocolV2ResourceIdentity({
40630
- commands,
40631
- resource,
40632
- chunkSize: normalizedChunkSize,
40633
- timeoutMs,
40634
- });
40635
- if (item)
40636
- inventory.push(item);
40637
- }
40638
- catch (_a) {
40639
- }
40640
- }
40641
- return inventory;
40642
- });
40643
- }
40644
- function normalizeHex$1(value, expectedLength, field) {
40645
- if (typeof value !== 'string') {
40646
- throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
40647
- }
40648
- const normalized = value.replace(/^0x/i, '').toLowerCase();
40649
- if (normalized.length !== expectedLength || !/^[0-9a-f]+$/.test(normalized)) {
40650
- throw new Error(`Invalid Pro2 resource ${field}: expected ${expectedLength} hexadecimal characters`);
40651
- }
40652
- return normalized;
40653
- }
40654
- function validateResource(value, index) {
40655
- if (!value || typeof value !== 'object') {
40656
- throw new Error(`Invalid Pro2 resource at stable[${index}]`);
40657
- }
40658
- const resource = value;
40659
- if (typeof resource.type !== 'string' || !RESOURCE_TYPE_SET.has(resource.type)) {
40660
- throw new Error(`Invalid Pro2 resource type at stable[${index}]`);
40661
- }
40662
- if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
40663
- throw new Error(`Invalid Pro2 resource url at stable[${index}]`);
40664
- }
40665
- if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
40666
- throw new Error(`Invalid Pro2 resource size at stable[${index}]`);
40667
- }
40668
- return {
40669
- type: resource.type,
40670
- url: resource.url,
40671
- size: Number(resource.size),
40672
- fileHash: normalizeHex$1(resource.fileHash, SHA256_HEX_LENGTH, 'fileHash'),
40673
- headerHash: normalizeHex$1(resource.headerHash, SHA3_512_HEX_LENGTH, 'headerHash'),
40674
- };
40675
- }
40676
- function validateBootResources(value) {
40677
- if (!value || typeof value !== 'object') {
40678
- throw new Error('Invalid Pro2 boot resources config');
40679
- }
40680
- const resource = value;
40681
- if (resource.required !== false) {
40682
- throw new Error('Invalid Pro2 boot resources required flag: expected false');
40683
- }
40684
- if (resource.target !== 'RES') {
40685
- throw new Error('Invalid Pro2 boot resources target: expected RES');
40686
- }
40687
- if (resource.manifestUrl !== undefined &&
40688
- (typeof resource.manifestUrl !== 'string' || !resource.manifestUrl.startsWith('https://'))) {
40689
- throw new Error('Invalid Pro2 boot resources manifestUrl');
40690
- }
40691
- if (!Array.isArray(resource.files) || resource.files.length === 0) {
40692
- throw new Error('Invalid Pro2 boot resources files');
40693
- }
40694
- const files = resource.files.map((value, index) => {
40695
- if (!value || typeof value !== 'object') {
40696
- throw new Error(`Invalid Pro2 boot resource file at files[${index}]`);
40697
- }
40698
- const file = value;
40699
- if (typeof file.url !== 'string' || !file.url.startsWith('https://')) {
40700
- throw new Error(`Invalid Pro2 boot resource url at files[${index}]`);
40701
- }
40702
- if (typeof file.devicePath !== 'string' ||
40703
- !file.devicePath.startsWith('vol0:/') ||
40704
- file.devicePath.includes('..') ||
40705
- file.devicePath.includes('\\')) {
40706
- throw new Error(`Invalid Pro2 boot resource devicePath at files[${index}]`);
40707
- }
40708
- if (!Number.isSafeInteger(file.size) || Number(file.size) <= 0) {
40709
- throw new Error(`Invalid Pro2 boot resource size at files[${index}]`);
40710
- }
40711
- return Object.assign(Object.assign({}, (typeof file.name === 'string' && file.name ? { name: file.name } : undefined)), { url: file.url, devicePath: file.devicePath, size: Number(file.size), fileHash: normalizeHex$1(file.fileHash, SHA256_HEX_LENGTH, `boot files[${index}].fileHash`) });
40712
- });
40713
- if (new Set(files.map(file => file.devicePath)).size !== files.length) {
40714
- throw new Error('Invalid Pro2 boot resources files: duplicate devicePath');
40715
- }
40716
- return Object.assign(Object.assign({ required: false, target: 'RES' }, (resource.manifestUrl ? { manifestUrl: resource.manifestUrl } : undefined)), { files });
40717
- }
40718
- function parseProtocolV2Resources(value) {
40719
- if (value === undefined)
40720
- return undefined;
40721
- if (!value ||
40722
- typeof value !== 'object' ||
40723
- !Array.isArray(value.stable)) {
40724
- throw new Error('Invalid Pro2 resources config: stable must be an array');
40725
- }
40726
- const config = value;
40727
- const stable = config.stable.map(validateResource);
40728
- const types = new Set(stable.map(resource => resource.type));
40729
- if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
40730
- throw new Error(`Invalid Pro2 resources config: stable must contain ${PROTOCOL_V2_RESOURCE_TYPES.length} unique resource types`);
40731
- }
40732
- for (const type of PROTOCOL_V2_RESOURCE_TYPES) {
40733
- if (!types.has(type)) {
40734
- throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
40735
- }
40736
- }
40737
- const boot = config.boot === undefined ? undefined : validateBootResources(config.boot);
40738
- return Object.assign({ stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
40739
- const resource = stable.find(item => item.type === type);
40740
- if (!resource) {
40741
- throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
40742
- }
40743
- return resource;
40744
- }) }, (boot ? { boot } : undefined));
40745
- }
40746
- function buildProtocolV2ResourceUpdatePlan({ resources, inventory, mode, forced = false, }) {
40747
- if (forced) {
40748
- return {
40749
- status: resources.length > 0 ? 'outdated' : 'valid',
40750
- resources: [...resources],
40751
- };
40752
- }
40753
- if (!inventory) {
40754
- return mode === 'bootloader-recovery'
40755
- ? {
40756
- status: resources.length > 0 ? 'outdated' : 'valid',
40757
- resources: [...resources],
40758
- }
40759
- : { status: 'unknown', resources: [] };
40760
- }
40761
- const inventoryByType = new Map(inventory.map(item => [item.type, item]));
40762
- const changedResources = resources.filter(resource => {
40763
- const current = inventoryByType.get(resource.type);
40764
- return (!current ||
40765
- current.size !== resource.size ||
40766
- current.headerHash.toLowerCase() !== resource.headerHash.toLowerCase());
40767
- });
40768
- return {
40769
- status: changedResources.length === 0 ? 'valid' : 'outdated',
40770
- resources: changedResources,
40771
- };
40772
- }
40773
- function bytesToHex$3(bytes) {
40774
- return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
40775
- }
40776
- function isProtocolV2ResourceFileValid(binary, resource) {
40777
- if (binary.byteLength !== resource.size)
40778
- return false;
40779
- return bytesToHex$3(sha256.sha256(new Uint8Array(binary))) === resource.fileHash.toLowerCase();
40780
- }
40781
-
40782
40706
  var _a$1;
40783
40707
  const FIRMWARE_UPDATE_CONFIG_FRESHNESS_MS = 5 * 60 * 1000;
40784
40708
  const Log$k = getLogger(exports.LoggerNames.Core);
@@ -40963,13 +40887,9 @@ class DataManager {
40963
40887
  this.lastCheckTimestamp = getTimeStamp();
40964
40888
  });
40965
40889
  }
40966
- static getProtocolV2Resources(deviceType = hdShared.EDeviceType.Pro2) {
40890
+ static getProtocolV2ResourceSource(deviceType = hdShared.EDeviceType.Pro2) {
40967
40891
  var _b, _c;
40968
- return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.stable;
40969
- }
40970
- static getProtocolV2BootResources(deviceType = hdShared.EDeviceType.Pro2) {
40971
- var _b, _c;
40972
- return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.boot;
40892
+ return (_c = (_b = this.deviceMap[deviceType]) === null || _b === void 0 ? void 0 : _b.resources) === null || _c === void 0 ? void 0 : _c.source;
40973
40893
  }
40974
40894
  static getProtobufMessages(schema = 'v1CurrentSchema') {
40975
40895
  return this.messages[schema];
@@ -47831,34 +47751,9 @@ class CheckAllFirmwareRelease extends BaseMethod {
47831
47751
  deviceType: state.identity.deviceType,
47832
47752
  });
47833
47753
  const resourceDeviceType = state.identity.deviceType === 'neo' ? hdShared.EDeviceType.Neo : hdShared.EDeviceType.Pro2;
47834
- const resources = DataManager.getProtocolV2Resources(resourceDeviceType);
47835
- let resourceStatus = 'unknown';
47836
- if (resources === null || resources === void 0 ? void 0 : resources.length) {
47837
- const loaderMode = state.status.mode === 'bootloader' || state.status.mode === 'romloader';
47838
- if (loaderMode) {
47839
- try {
47840
- const inventory = yield readProtocolV2ResourceInventory({
47841
- commands: this.device.getCommands(),
47842
- resources,
47843
- });
47844
- resourceStatus = buildProtocolV2ResourceUpdatePlan({
47845
- resources,
47846
- inventory,
47847
- mode: 'bootloader-recovery',
47848
- }).status;
47849
- }
47850
- catch (_b) {
47851
- resourceStatus = buildProtocolV2ResourceUpdatePlan({
47852
- resources,
47853
- mode: 'bootloader-recovery',
47854
- }).status;
47855
- }
47856
- }
47857
- }
47858
- const targetsToUpdate = [
47859
- ...plan.targetsToUpdate,
47860
- ...(resourceStatus === 'outdated' ? ['resource'] : []),
47861
- ];
47754
+ const resourceSource = DataManager.getProtocolV2ResourceSource(resourceDeviceType);
47755
+ const resourceStatus = 'unknown';
47756
+ const targetsToUpdate = [...plan.targetsToUpdate];
47862
47757
  const firmwareStatus = plan.status === 'unavailable' ? 'unknown' : plan.status;
47863
47758
  const emptyRelease = 'none';
47864
47759
  return Object.assign(Object.assign({ firmware: {
@@ -47872,7 +47767,7 @@ class CheckAllFirmwareRelease extends BaseMethod {
47872
47767
  }, bootloader: {
47873
47768
  status: 'valid',
47874
47769
  release: emptyRelease,
47875
- }, features, protocol: 'V2', deviceType: state.identity.deviceType }, plan), { resourceStatus, hasUpgrade: plan.hasUpgrade || resourceStatus === 'outdated', targetsToUpdate });
47770
+ }, features, protocol: 'V2', deviceType: state.identity.deviceType }, plan), { resourceStatus, resourceManifestUrl: resourceSource === null || resourceSource === void 0 ? void 0 : resourceSource.manifestUrl, hasUpgrade: plan.hasUpgrade, targetsToUpdate });
47876
47771
  });
47877
47772
  }
47878
47773
  }
@@ -51185,6 +51080,14 @@ const PROTOCOL_V2_REMOTE_COMPONENT_TARGETS = {
51185
51080
  },
51186
51081
  };
51187
51082
  const PROTOCOL_V2_FIRMWARE_STAGING_PATHS = new Set(Object.values(PROTOCOL_V2_REMOTE_COMPONENT_TARGETS).map(target => `${PROTOCOL_V2_FIRMWARE_STAGING_VOLUME}${target.fileName}`));
51083
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH = 'vol0:/loaders/bootloader/boot_resource.okpkg';
51084
+ const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
51085
+ const resolveProtocolV2ResourceWritePath = (devicePath) => {
51086
+ const normalizedPath = devicePath.replace(/^vol0:(?!\/)/i, 'vol0:/').toLowerCase();
51087
+ return normalizedPath === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH
51088
+ ? PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH
51089
+ : devicePath;
51090
+ };
51188
51091
  const PROTOCOL_V2_UPDATE_TARGET_BY_TARGET_ID = new Map([
51189
51092
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER, 'boot'],
51190
51093
  [ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1, 'app_v1'],
@@ -51488,7 +51391,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51488
51391
  });
51489
51392
  }
51490
51393
  runProtocolV2() {
51491
- var _a, _b, _c, _d, _e;
51394
+ var _a, _b, _c, _d, _e, _f;
51492
51395
  return __awaiter(this, void 0, void 0, function* () {
51493
51396
  yield this.captureProtocolV2PhysicalIdentity();
51494
51397
  const deviceFeatures = yield this.getProtocolV2DeviceFeatures();
@@ -51507,8 +51410,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51507
51410
  const hasExplicitResourceFiles = !!((_c = this.params.resourceFiles) === null || _c === void 0 ? void 0 : _c.length);
51508
51411
  const wantsStableResources = !!((_d = this.params.targetsToUpdate) === null || _d === void 0 ? void 0 : _d.includes('resource'));
51509
51412
  const wantsBootResources = !!((_e = this.params.targetsToUpdate) === null || _e === void 0 ? void 0 : _e.includes('boot_resources'));
51510
- const needsRemoteResources = !hasExplicitResourceFiles && wantsStableResources;
51511
- const needsRemoteBootResources = !hasExplicitResourceFiles && (wantsBootResources || wantsStableResources);
51413
+ const needsPreparedResources = !hasExplicitResourceFiles && (wantsStableResources || wantsBootResources);
51512
51414
  let fwBinaryMap = [];
51513
51415
  let bootloaderBinary = null;
51514
51416
  let installItems;
@@ -51519,17 +51421,20 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51519
51421
  fwBinaryMap = this.collectExplicitTargetBinaries();
51520
51422
  bootloaderBinary = this.prepareBootloaderBinary();
51521
51423
  const needsRemoteFirmware = !this.hasExplicitProtocolV2Payload(fwBinaryMap);
51522
- if ((needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) &&
51424
+ if (needsPreparedResources) {
51425
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 resource manifest must be prepared by the external firmware host', {
51426
+ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
51427
+ });
51428
+ }
51429
+ if (needsRemoteFirmware &&
51523
51430
  (this.params.artifactReader ||
51524
51431
  DataManager.getSettings('firmwareManifestMode') === 'external-only')) {
51525
51432
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 firmware artifacts must be prepared by the external firmware host', {
51526
51433
  firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
51527
51434
  });
51528
51435
  }
51529
- if (needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) {
51530
- yield DataManager.forceReloadData({
51531
- requireResources: needsRemoteResources || needsRemoteBootResources,
51532
- });
51436
+ if (needsRemoteFirmware) {
51437
+ yield DataManager.forceReloadData();
51533
51438
  }
51534
51439
  if (needsRemoteFirmware) {
51535
51440
  const remoteBinaries = yield this.prepareRemoteProtocolV2Binaries(firmwareType, deviceFeatures);
@@ -51537,15 +51442,15 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51537
51442
  fwBinaryMap = remoteBinaries.fwBinaryMap;
51538
51443
  installItems = remoteBinaries.installItems;
51539
51444
  }
51540
- const bootResourceFiles = yield this.prepareProtocolV2BootResources();
51541
- if (bootResourceFiles === null || bootResourceFiles === void 0 ? void 0 : bootResourceFiles.length) {
51542
- resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, bootResourceFiles);
51543
- }
51544
- if (!needsRemoteResources) {
51545
- this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
51546
- }
51445
+ this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
51547
51446
  }
51548
51447
  catch (err) {
51448
+ if (typeof err === 'object' &&
51449
+ err !== null &&
51450
+ 'params' in err &&
51451
+ ((_f = err.params) === null || _f === void 0 ? void 0 : _f.firmwareUpdateCode) === 'FirmwareArtifactsNotPrepared') {
51452
+ throw err;
51453
+ }
51549
51454
  if (err instanceof hdShared.HardwareError && err.errorCode === hdShared.HardwareErrorCode.NetworkError) {
51550
51455
  throw err;
51551
51456
  }
@@ -51554,29 +51459,9 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51554
51459
  if (!bootloaderBinary &&
51555
51460
  fwBinaryMap.length === 0 &&
51556
51461
  !(installItems === null || installItems === void 0 ? void 0 : installItems.length) &&
51557
- !(resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length) &&
51558
- !needsRemoteResources) {
51462
+ !(resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length)) {
51559
51463
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, 'No firmware to update');
51560
51464
  }
51561
- if (needsRemoteResources) {
51562
- const enteredBootloader = yield this.enterProtocolV2BootloaderMode();
51563
- try {
51564
- const stableResources = yield this.prepareProtocolV2ResourceBundles();
51565
- resourceBundles = this.mergeProtocolV2ResourceBundles(resourceBundles, stableResources);
51566
- this.postTipMessage(exports.FirmwareUpdateTipMessage.FinishDownloadFirmware);
51567
- }
51568
- catch (err) {
51569
- if (enteredBootloader) {
51570
- try {
51571
- yield this.exitProtocolV2BootloaderToNormal();
51572
- }
51573
- catch (restoreError) {
51574
- Log$6.warn('[FirmwareUpdateV4] failed to restore App mode after resource preparation error:', restoreError);
51575
- }
51576
- }
51577
- throw normalizeFirmwarePreparationError(err);
51578
- }
51579
- }
51580
51465
  return this.executeProtocolV2Update(Object.assign(Object.assign({ fwBinaryMap,
51581
51466
  bootloaderBinary }, (installItems ? { installItems } : undefined)), ((resourceBundles === null || resourceBundles === void 0 ? void 0 : resourceBundles.length) ? { resourceBundles } : undefined)));
51582
51467
  });
@@ -51984,102 +51869,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
51984
51869
  };
51985
51870
  });
51986
51871
  }
51987
- getProtocolV2DeviceType() {
51988
- const deviceType = this.device.getCurrentDeviceType();
51989
- if (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo)
51990
- return deviceType;
51991
- throw new Error(`Unsupported Protocol V2 device type: ${deviceType}`);
51992
- }
51993
- prepareProtocolV2BootResources() {
51994
- var _a, _b, _c, _d, _e;
51995
- return __awaiter(this, void 0, void 0, function* () {
51996
- const wantsStableResources = !!((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource'));
51997
- const wantsBootResources = !!((_b = this.params.targetsToUpdate) === null || _b === void 0 ? void 0 : _b.includes('boot_resources'));
51998
- if (!wantsStableResources && !wantsBootResources) {
51999
- return undefined;
52000
- }
52001
- if ((_c = this.params.resourceFiles) === null || _c === void 0 ? void 0 : _c.length) {
52002
- return undefined;
52003
- }
52004
- const resource = DataManager.getProtocolV2BootResources(this.getProtocolV2DeviceType());
52005
- if (!resource) {
52006
- if (wantsBootResources) {
52007
- throw new Error('Missing Protocol V2 boot resources configuration');
52008
- }
52009
- Log$6.debug('[FirmwareUpdateV4] no boot resources configured; continue with stable resources');
52010
- return undefined;
52011
- }
52012
- const files = [];
52013
- for (const file of resource.files) {
52014
- const isCurrent = !this.params.forcedUpdateRes && (yield this.isProtocolV2BootResourceCurrent(file));
52015
- if (isCurrent) {
52016
- Log$6.log(`[FirmwareUpdateV4] boot resource unchanged, skipping ${file.devicePath}`);
52017
- }
52018
- else {
52019
- Log$6.log(`[FirmwareUpdateV4] downloading boot resource ${file.devicePath}`);
52020
- const { binary } = yield getSysResourceBinary(file.url);
52021
- if (!isProtocolV2ResourceFileValid(binary, file)) {
52022
- throw new Error(`Boot resource file verification failed: ${file.devicePath}`);
52023
- }
52024
- files.push({
52025
- name: (_e = (_d = file.name) !== null && _d !== void 0 ? _d : file.devicePath.split('/').pop()) !== null && _e !== void 0 ? _e : file.devicePath,
52026
- binary,
52027
- devicePath: file.devicePath,
52028
- });
52029
- }
52030
- }
52031
- return files;
52032
- });
52033
- }
52034
- isProtocolV2BootResourceCurrent(file) {
52035
- var _a, _b, _c, _d;
52036
- return __awaiter(this, void 0, void 0, function* () {
52037
- try {
52038
- const commands = this.device.getCommands();
52039
- const pathInfo = yield commands.typedCall('FilesystemPathInfoQuery', 'FilesystemPathInfo', { path: file.devicePath }, { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT });
52040
- const size = toProtocolV2FiniteNumber((_a = pathInfo.message) === null || _a === void 0 ? void 0 : _a.size);
52041
- if (!((_b = pathInfo.message) === null || _b === void 0 ? void 0 : _b.exist) ||
52042
- ((_c = pathInfo.message) === null || _c === void 0 ? void 0 : _c.directory) ||
52043
- !Number.isSafeInteger(size) ||
52044
- size !== file.size) {
52045
- return false;
52046
- }
52047
- const digest = sha256.sha256.create();
52048
- const chunkSize = this.getProtocolV2FirmwareChunkSize('write');
52049
- let offset = 0;
52050
- while (offset < file.size) {
52051
- const response = yield commands.typedCall('FilesystemFileRead', 'FilesystemFile', {
52052
- file: { path: file.devicePath, offset, total_size: 0 },
52053
- chunk_len: Math.min(chunkSize, file.size - offset),
52054
- }, { timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT });
52055
- const data = toProtocolV2Bytes((_d = response.message) === null || _d === void 0 ? void 0 : _d.data);
52056
- if (data.byteLength === 0)
52057
- return false;
52058
- const consumed = data.subarray(0, Math.min(data.byteLength, file.size - offset));
52059
- digest.update(consumed);
52060
- offset += consumed.byteLength;
52061
- }
52062
- return bytesToHex(digest.digest()) === normalizeProtocolV2Hex(file.fileHash);
52063
- }
52064
- catch (error) {
52065
- Log$6.debug(`[FirmwareUpdateV4] unable to compare boot resource ${file.devicePath}; scheduling rewrite`, error);
52066
- return false;
52067
- }
52068
- });
52069
- }
52070
- mergeProtocolV2ResourceBundles(...groups) {
52071
- const merged = groups.flatMap(group => group !== null && group !== void 0 ? group : []);
52072
- if (!merged.length)
52073
- return undefined;
52074
- const seenPaths = new Set();
52075
- for (const bundle of merged) {
52076
- if (seenPaths.has(bundle.devicePath)) {
52077
- throw new Error(`Duplicate Protocol V2 resource devicePath: ${bundle.devicePath}`);
52078
- }
52079
- seenPaths.add(bundle.devicePath);
52080
- }
52081
- return merged;
52082
- }
52083
51872
  prepareExplicitProtocolV2ResourceFiles() {
52084
51873
  var _a;
52085
51874
  const files = (_a = this.params.resourceFiles) !== null && _a !== void 0 ? _a : [];
@@ -52110,52 +51899,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52110
51899
  }
52111
51900
  return prepared;
52112
51901
  }
52113
- prepareProtocolV2ResourceBundles() {
52114
- var _a;
52115
- return __awaiter(this, void 0, void 0, function* () {
52116
- if (!((_a = this.params.targetsToUpdate) === null || _a === void 0 ? void 0 : _a.includes('resource'))) {
52117
- return undefined;
52118
- }
52119
- const resources = DataManager.getProtocolV2Resources(this.getProtocolV2DeviceType());
52120
- if (!(resources === null || resources === void 0 ? void 0 : resources.length)) {
52121
- throw new Error('Missing Pro2 stable resource configuration');
52122
- }
52123
- const inventory = this.params.forcedUpdateRes
52124
- ? undefined
52125
- : yield readProtocolV2ResourceInventory({
52126
- commands: this.device.getCommands(),
52127
- resources,
52128
- chunkSize: this.getProtocolV2FirmwareChunkSize('write'),
52129
- timeoutMs: PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT,
52130
- });
52131
- const plan = buildProtocolV2ResourceUpdatePlan({
52132
- resources,
52133
- inventory,
52134
- mode: 'bootloader-recovery',
52135
- forced: this.params.forcedUpdateRes,
52136
- });
52137
- Log$6.log(`[FirmwareUpdateV4] Protocol V2 resource plan mode=bootloader-recovery status=${plan.status} count=${plan.resources.length}`);
52138
- const bundles = [];
52139
- for (const resource of plan.resources) {
52140
- bundles.push(yield this.downloadProtocolV2Resource(resource));
52141
- }
52142
- return bundles;
52143
- });
52144
- }
52145
- downloadProtocolV2Resource(resource) {
52146
- return __awaiter(this, void 0, void 0, function* () {
52147
- Log$6.log(`[FirmwareUpdateV4] downloading Pro2 resource ${resource.type}`);
52148
- const { binary } = yield getSysResourceBinary(resource.url);
52149
- if (!isProtocolV2ResourceFileValid(binary, resource)) {
52150
- throw new Error(`Pro2 resource file verification failed: ${resource.type}`);
52151
- }
52152
- return {
52153
- name: `${resource.type}.okpkg`,
52154
- binary,
52155
- devicePath: PROTOCOL_V2_RESOURCE_DEVICE_PATHS[resource.type],
52156
- };
52157
- });
52158
- }
52159
51902
  getProtocolV2ResourceFilePath(path) {
52160
51903
  if (path.startsWith('vol'))
52161
51904
  return path;
@@ -52347,13 +52090,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52347
52090
  }
52348
52091
  buildProtocolV2ExecutionPhases({ installSources, resourceSources, }) {
52349
52092
  const phases = [];
52350
- if (resourceSources.length > 0) {
52351
- phases.push({
52352
- kind: 'resource-sync',
52353
- installSources: [],
52354
- resourceSources,
52355
- });
52356
- }
52357
52093
  const bootloaderSources = installSources.filter(source => source.kind === 'bootloader');
52358
52094
  if (bootloaderSources.length > 0) {
52359
52095
  phases.push({
@@ -52366,6 +52102,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52366
52102
  resourceSources: [],
52367
52103
  });
52368
52104
  }
52105
+ if (resourceSources.length > 0) {
52106
+ phases.push({
52107
+ kind: 'resource-sync',
52108
+ installSources: [],
52109
+ resourceSources,
52110
+ });
52111
+ }
52369
52112
  const componentSources = installSources.filter(source => source.kind !== 'bootloader');
52370
52113
  if (componentSources.length > 0) {
52371
52114
  phases.push({
@@ -52464,13 +52207,14 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
52464
52207
  this.postTipMessage(exports.FirmwareUpdateTipMessage.StartTransferData);
52465
52208
  let processedSize = 0;
52466
52209
  for (const resource of resourcesToSync) {
52210
+ const writePath = resolveProtocolV2ResourceWritePath(resource.devicePath);
52467
52211
  processedSize = yield this.protocolV2SourceUpdateProcess({
52468
52212
  source: resource.source,
52469
- filePath: resource.devicePath,
52213
+ filePath: writePath,
52470
52214
  processedSize,
52471
52215
  totalSize,
52472
52216
  });
52473
- yield this.verifyProtocolV2StagedFile(resource.devicePath, resource.source.size);
52217
+ yield this.verifyProtocolV2StagedFile(writePath, resource.source.size);
52474
52218
  }
52475
52219
  const stagedInstallTargets = [];
52476
52220
  for (const item of installSources) {
@@ -53460,7 +53204,7 @@ class DeviceUploadWallpaper extends BaseMethod {
53460
53204
  }
53461
53205
 
53462
53206
  const FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE = 60802;
53463
- const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
53207
+ const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$1 = 60805;
53464
53208
  const FILESYSTEM_DIR_LIST_MESSAGE_TYPE = 60808;
53465
53209
  const NFT_UPDATE_MESSAGE_TYPE = 61500;
53466
53210
  class DeviceUploadNft extends BaseMethod {
@@ -53489,7 +53233,7 @@ class DeviceUploadNft extends BaseMethod {
53489
53233
  assertCapabilities() {
53490
53234
  return __awaiter(this, void 0, void 0, function* () {
53491
53235
  const protocolInfo = yield this.device.ensureProtocolV2RuntimeContext();
53492
- const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE);
53236
+ const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$1);
53493
53237
  const hasPathInfo = supportsProtocolV2Message(protocolInfo, FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE);
53494
53238
  const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
53495
53239
  const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
@@ -53624,6 +53368,8 @@ class FileWrite extends BaseMethod {
53624
53368
 
53625
53369
  const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
53626
53370
  const PORTFOLIO_CHUNK_SIZE = 2048;
53371
+ const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
53372
+ const PORTFOLIO_UPDATE_MESSAGE_TYPE = 61400;
53627
53373
  class UploadPortfolio extends FileWrite {
53628
53374
  init() {
53629
53375
  const { packageBytes, timeoutMs } = this.payload;
@@ -53637,6 +53383,12 @@ class UploadPortfolio extends FileWrite {
53637
53383
  run: { get: () => super.run }
53638
53384
  });
53639
53385
  return __awaiter(this, void 0, void 0, function* () {
53386
+ const protocolInfo = yield this.device.ensureProtocolV2RuntimeContext();
53387
+ const hasFileWrite = supportsProtocolV2Message(protocolInfo, FILESYSTEM_FILE_WRITE_MESSAGE_TYPE);
53388
+ const hasPortfolioUpdate = supportsProtocolV2Message(protocolInfo, PORTFOLIO_UPDATE_MESSAGE_TYPE);
53389
+ if (!hasFileWrite || !hasPortfolioUpdate) {
53390
+ throw hdShared.createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
53391
+ }
53640
53392
  const stagedFile = yield _super.run.call(this);
53641
53393
  this.throwIfAborted();
53642
53394
  yield this.device.commands.typedCall('PortfolioUpdate', 'Success', {});
@@ -65178,11 +64930,15 @@ exports.normalizeSafetyCheckLevel = normalizeSafetyCheckLevel;
65178
64930
  exports.normalizeVersionArray = normalizeVersionArray;
65179
64931
  exports.parseConnectSettings = parseConnectSettings;
65180
64932
  exports.parseMessage = parseMessage;
64933
+ exports.parseProtocolV2ResourceManifest = parseProtocolV2ResourceManifest;
65181
64934
  exports.patchFeatures = patchFeatures;
65182
64935
  exports.preloadSessionCache = preloadSessionCache;
64936
+ exports.prepareProtocolV2ResourceFiles = prepareProtocolV2ResourceFiles;
65183
64937
  exports.projectDeviceStateFeatures = projectFeatures;
65184
64938
  exports.registerFirmwareUpdateHostBinding = registerFirmwareUpdateHostBinding;
64939
+ exports.resolveProtocolV2ResourceManifestFileUrl = resolveProtocolV2ResourceManifestFileUrl;
65185
64940
  exports.safeThrowError = safeThrowError;
64941
+ exports.selectProtocolV2ResourceManifestFiles = selectProtocolV2ResourceManifestFiles;
65186
64942
  exports.setLoggerPostMessage = setLoggerPostMessage;
65187
64943
  exports.supportInputPinOnSoftware = supportInputPinOnSoftware;
65188
64944
  exports.switchTransport = switchTransport;