@onekeyfe/hd-core 1.2.8-alpha.127 → 1.2.8-alpha.129

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 (31) hide show
  1. package/__tests__/device-lifecycle-events.test.ts +96 -0
  2. package/__tests__/firmware-update/firmware-artifact-source.test.ts +11 -0
  3. package/__tests__/protocol-v2-resources.test.ts +14 -0
  4. package/__tests__/protocol-v2.test.ts +306 -14
  5. package/__tests__/protocolV2FileWrite.test.ts +434 -7
  6. package/dist/api/FileWrite.d.ts.map +1 -1
  7. package/dist/api/FirmwareUpdateV4.d.ts +0 -1
  8. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  9. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -1
  10. package/dist/api/helpers/protocolV2FileWrite.d.ts +5 -1
  11. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  12. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  13. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  14. package/dist/device/Device.d.ts +1 -0
  15. package/dist/device/Device.d.ts.map +1 -1
  16. package/dist/events/ui-request.d.ts +1 -0
  17. package/dist/events/ui-request.d.ts.map +1 -1
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +616 -382
  20. package/dist/protocols/protocol-v2/resources.d.ts +1 -0
  21. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  22. package/package.json +4 -4
  23. package/src/api/FileWrite.ts +7 -1
  24. package/src/api/FirmwareUpdateV4.ts +56 -30
  25. package/src/api/firmware/FirmwareArtifactSource.ts +12 -3
  26. package/src/api/helpers/protocolV2FileWrite.ts +375 -111
  27. package/src/api/protocol-v2/DeviceUploadNft.ts +6 -1
  28. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +15 -1
  29. package/src/device/Device.ts +18 -2
  30. package/src/events/ui-request.ts +1 -0
  31. package/src/protocols/protocol-v2/resources.ts +6 -1
package/dist/index.js CHANGED
@@ -235,15 +235,24 @@ const readFirmwareByteSourceFully = (source) => __awaiter(void 0, void 0, void 0
235
235
  const readVerifiedFirmwareArtifact = ({ artifact, reader, }) => __awaiter(void 0, void 0, void 0, function* () {
236
236
  assertFirmwareArtifactReference(artifact);
237
237
  const source = yield ReaderFirmwareByteSource.open(artifact, reader);
238
+ const hasher = sha256.sha256.create();
238
239
  try {
239
- const binary = yield readFirmwareByteSourceFully(source);
240
- const actualSha256 = utils.bytesToHex(sha256.sha256(new Uint8Array(binary)));
240
+ const bytes = new Uint8Array(source.size);
241
+ let offset = 0;
242
+ while (offset < source.size) {
243
+ const length = Math.min(MAX_FIRMWARE_ARTIFACT_READ_BYTES, source.size - offset);
244
+ bytes.set(new Uint8Array(yield source.readAt(offset, length)), offset);
245
+ hasher.update(bytes.subarray(offset, offset + length));
246
+ offset += length;
247
+ }
248
+ const actualSha256 = utils.bytesToHex(hasher.digest());
241
249
  if (actualSha256 !== artifact.sha256.toLowerCase()) {
242
250
  return artifactError('FirmwareArtifactReceiptMismatch', 'Firmware artifact bytes do not match the approved SHA-256 receipt');
243
251
  }
244
- return binary;
252
+ return bytes.buffer;
245
253
  }
246
254
  finally {
255
+ hasher.destroy();
247
256
  yield source.close().catch(() => undefined);
248
257
  }
249
258
  });
@@ -43071,6 +43080,7 @@ const findLatestRelease = (releases) => {
43071
43080
  const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH = 'vol0:/loaders/bootloader/boot_resource.okpkg';
43072
43081
  const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
43073
43082
  const PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE = 0x5f90;
43083
+ const PROTOCOL_V2_RESOURCE_PACKAGE_COMPARE_SIZE = 0x280;
43074
43084
  const PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_VERSION = 1;
43075
43085
  const PROTOCOL_V2_RESOURCE_PACKAGE_FLEXIBLE_OFFSET = 0x6c;
43076
43086
  const PROTOCOL_V2_RESOURCE_PACKAGE_FLEXIBLE_SIZE = 64;
@@ -43154,7 +43164,7 @@ function readResourceDevicePath(bytes) {
43154
43164
  return path;
43155
43165
  }
43156
43166
  function parseProtocolV2ResourcePackageHeader(bytes, packageSize) {
43157
- if (bytes.byteLength < PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE) {
43167
+ if (bytes.byteLength < PROTOCOL_V2_RESOURCE_PACKAGE_COMPARE_SIZE) {
43158
43168
  throw new Error('Pro2 RESOURCE package is shorter than its header');
43159
43169
  }
43160
43170
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
@@ -43184,6 +43194,9 @@ function parseProtocolV2ResourcePackageHeader(bytes, packageSize) {
43184
43194
  }
43185
43195
  function parseProtocolV2ResourcePackage(binary) {
43186
43196
  const bytes = binary instanceof Uint8Array ? binary : new Uint8Array(binary);
43197
+ if (bytes.byteLength < PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE) {
43198
+ throw new Error('Pro2 RESOURCE package is shorter than its header');
43199
+ }
43187
43200
  return parseProtocolV2ResourcePackageHeader(bytes, bytes.byteLength);
43188
43201
  }
43189
43202
 
@@ -45546,6 +45559,7 @@ class Device extends events.exports {
45546
45559
  super();
45547
45560
  this.deviceConnector = null;
45548
45561
  this.deviceAcquired = false;
45562
+ this.releasedBleConnection = false;
45549
45563
  this.connectionAttempt = 0;
45550
45564
  this.interruptedAttempt = null;
45551
45565
  this.stateStore = new DeviceStateStore();
@@ -45650,6 +45664,7 @@ class Device extends events.exports {
45650
45664
  return __awaiter(this, void 0, void 0, function* () {
45651
45665
  const attempt = this.connectionAttempt;
45652
45666
  this.throwIfInterruptedByUser();
45667
+ this.releasedBleConnection = false;
45653
45668
  const env = DataManager.getSettings('env');
45654
45669
  const mainIdKey = DataManager.isBleConnect(env) ? 'id' : 'session';
45655
45670
  const previousProtocol = this.originalDescriptor.protocolType;
@@ -45729,6 +45744,7 @@ class Device extends events.exports {
45729
45744
  const env = DataManager.getSettings('env');
45730
45745
  if ((this.isUsedHere() && !this.keepSession && this.mainId) ||
45731
45746
  (this.mainId && DataManager.isBleConnect(env))) {
45747
+ this.releasedBleConnection = false;
45732
45748
  if (this.pendingCallbackPromise) {
45733
45749
  try {
45734
45750
  Log$i.debug('Waiting for callback tasks to complete before releasing device (in release method)');
@@ -45750,7 +45766,12 @@ class Device extends events.exports {
45750
45766
  }
45751
45767
  }
45752
45768
  try {
45753
- yield ((_a = this.deviceConnector) === null || _a === void 0 ? void 0 : _a.release(this.mainId, false, this.keepSession));
45769
+ const deviceType = this.getCurrentDeviceType();
45770
+ const keepBleConnection = (env === 'react-native' || env === 'desktop-web-ble') &&
45771
+ this.isProtocolV2() &&
45772
+ (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo);
45773
+ yield ((_a = this.deviceConnector) === null || _a === void 0 ? void 0 : _a.release(this.mainId, false, this.keepSession || keepBleConnection));
45774
+ this.releasedBleConnection = keepBleConnection;
45754
45775
  }
45755
45776
  catch (err) {
45756
45777
  Log$i.error('[Device] release error: ', err);
@@ -46359,6 +46380,7 @@ class Device extends events.exports {
46359
46380
  }
46360
46381
  markTransportDisconnected() {
46361
46382
  this.deviceAcquired = false;
46383
+ this.releasedBleConnection = false;
46362
46384
  this.invalidateProtocolV2RuntimeState();
46363
46385
  }
46364
46386
  invalidateAfterWipe() {
@@ -46583,7 +46605,7 @@ class Device extends events.exports {
46583
46605
  Log$i.debug('Protocol V2 fallback cancel error', cancelError);
46584
46606
  }));
46585
46607
  }
46586
- else if (!acquired) {
46608
+ else if (!acquired && !this.releasedBleConnection) {
46587
46609
  if (mainId && (deviceConnector === null || deviceConnector === void 0 ? void 0 : deviceConnector.disconnect)) {
46588
46610
  Log$i.debug('interruptionFromUser: disconnecting device without acquire, mainId:', mainId);
46589
46611
  yield deviceConnector.disconnect(mainId);
@@ -52533,11 +52555,276 @@ const INSTALLABLE_FIRMWARE_TARGET_IDS = new Set([
52533
52555
  ]);
52534
52556
  new Map(Object.entries(ProtocolV2FirmwareTargetType).flatMap(([key, value]) => INSTALLABLE_FIRMWARE_TARGET_IDS.has(value) ? [[key, value]] : []));
52535
52557
 
52558
+ const isProtocolV2UiEnabled = (method) => method.protocolV2UiMode !== 'none';
52559
+ const getDisplayState = (value) => {
52560
+ const visited = new Set();
52561
+ let seen = false;
52562
+ let enabled = false;
52563
+ const visit = (current) => {
52564
+ if (current == null || typeof current !== 'object')
52565
+ return;
52566
+ if (visited.has(current))
52567
+ return;
52568
+ visited.add(current);
52569
+ if (Array.isArray(current)) {
52570
+ current.forEach(visit);
52571
+ return;
52572
+ }
52573
+ Object.entries(current).forEach(([key, item]) => {
52574
+ if ((key === 'show_display' || key === 'showOnOneKey') && typeof item === 'boolean') {
52575
+ seen = true;
52576
+ enabled || (enabled = item);
52577
+ return;
52578
+ }
52579
+ visit(item);
52580
+ });
52581
+ };
52582
+ visit(value);
52583
+ return { seen, enabled };
52584
+ };
52585
+ const createMethodInteraction = (reason, operation) => ({
52586
+ request: 'button',
52587
+ source: 'method-lifecycle',
52588
+ reason,
52589
+ completion: 'operation-completed',
52590
+ deviceOnly: true,
52591
+ operation,
52592
+ });
52593
+ const hasCipherConfirmation = (value) => {
52594
+ if (Array.isArray(value))
52595
+ return value.some(hasCipherConfirmation);
52596
+ if (value == null || typeof value !== 'object')
52597
+ return false;
52598
+ const params = value;
52599
+ return params.encrypt === true ? params.ask_on_encrypt === true : params.ask_on_decrypt === true;
52600
+ };
52601
+ const resolveProtocolV2UiInteraction = (method) => {
52602
+ if (method.protocolV2UiInteraction)
52603
+ return method.protocolV2UiInteraction;
52604
+ const operation = method.name;
52605
+ if (!operation)
52606
+ return undefined;
52607
+ const isAddress = /getAddress(?:ByLoop)?$/i.test(operation);
52608
+ const isPublicKey = /getPublicKey$/i.test(operation);
52609
+ if (isAddress || isPublicKey) {
52610
+ const paramsDisplay = getDisplayState(method.params);
52611
+ const payloadDisplay = getDisplayState(method.payload);
52612
+ const display = paramsDisplay.seen ? paramsDisplay : payloadDisplay;
52613
+ const shouldDisplay = display.seen ? display.enabled : isAddress;
52614
+ if (!shouldDisplay)
52615
+ return undefined;
52616
+ return createMethodInteraction(isAddress ? 'address-confirmation' : 'public-key-confirmation', operation);
52617
+ }
52618
+ if (/sign/i.test(operation) || /verifyMessage$/i.test(operation) || operation === 'lnurlAuth') {
52619
+ return createMethodInteraction('signing-confirmation', operation);
52620
+ }
52621
+ if (operation === 'nostrEncryptMessage' || operation === 'nostrDecryptMessage') {
52622
+ const display = getDisplayState(method.params);
52623
+ if (!display.enabled)
52624
+ return undefined;
52625
+ return createMethodInteraction('signing-confirmation', operation);
52626
+ }
52627
+ if (operation === 'cipherKeyValue' && hasCipherConfirmation(method.params)) {
52628
+ return createMethodInteraction('signing-confirmation', operation);
52629
+ }
52630
+ return undefined;
52631
+ };
52632
+ class ProtocolV2UiInteractionCoordinator {
52633
+ constructor(device, postMessage) {
52634
+ this.opened = false;
52635
+ this.closed = false;
52636
+ this.device = device;
52637
+ this.postMessage = postMessage;
52638
+ }
52639
+ enterMethodInteraction(interaction) {
52640
+ var _a, _b, _c, _d;
52641
+ this.methodInteraction = interaction;
52642
+ if (!interaction)
52643
+ return;
52644
+ const { request } = interaction, metadata = __rest(interaction, ["request"]);
52645
+ const interactionMeta = (_b = (_a = this.device).createProtocolV2UiPhaseMetadata) === null || _b === void 0 ? void 0 : _b.call(_a, request === 'pin' ? 'pin' : 'button', 'start');
52646
+ this.emit(request === 'pin' ? UI_REQUEST.REQUEST_PIN : UI_REQUEST.REQUEST_BUTTON, Object.assign(Object.assign({ device: this.device.toMessageObject() }, metadata), (interactionMeta ? { interaction: interactionMeta } : {})), `method:${request}:${interaction.reason}:${(_c = interaction.page) !== null && _c !== void 0 ? _c : ''}:${(_d = interaction.operation) !== null && _d !== void 0 ? _d : ''}`);
52647
+ }
52648
+ enterUnlockInteraction(method) {
52649
+ var _a, _b;
52650
+ const interaction = (_b = (_a = this.device).createProtocolV2UiPhaseMetadata) === null || _b === void 0 ? void 0 : _b.call(_a, 'pin', 'start');
52651
+ this.emit(UI_REQUEST.REQUEST_PIN, Object.assign({ device: this.device.toMessageObject(), source: 'unlock-coordinator', reason: 'device-locked', deviceOnly: true, method }, (interaction ? { interaction } : {})), `unlock:${method !== null && method !== void 0 ? method : ''}`);
52652
+ return interaction;
52653
+ }
52654
+ resumeMethodInteraction() {
52655
+ this.enterMethodInteraction(this.methodInteraction);
52656
+ }
52657
+ close(options = {}) {
52658
+ var _a, _b, _c, _d;
52659
+ if ((!options.protocolV2Operation && !this.device.isProtocolV2()) ||
52660
+ (!options.ensureOperationClose &&
52661
+ !this.opened &&
52662
+ !((_b = (_a = this.device).hasOpenProtocolV2UiInteraction) === null || _b === void 0 ? void 0 : _b.call(_a))) ||
52663
+ this.closed)
52664
+ return false;
52665
+ this.closed = true;
52666
+ const interaction = (_d = (_c = this.device).finishProtocolV2UiInteraction) === null || _d === void 0 ? void 0 : _d.call(_c, 'succeeded', {
52667
+ ensureMetadata: options.ensureOperationClose,
52668
+ });
52669
+ const device = this.device.toMessageObject();
52670
+ this.postMessage(interaction
52671
+ ? createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW, Object.assign(Object.assign({}, interaction), (device ? { device } : {})))
52672
+ : createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW));
52673
+ return true;
52674
+ }
52675
+ emit(type, payload, phase) {
52676
+ if (!this.device.isProtocolV2() || this.closed || this.currentPhase === phase)
52677
+ return;
52678
+ this.currentPhase = phase;
52679
+ this.opened = true;
52680
+ const message = type === UI_REQUEST.REQUEST_BUTTON
52681
+ ? createUiMessage(UI_REQUEST.REQUEST_BUTTON, payload)
52682
+ : createUiMessage(UI_REQUEST.REQUEST_PIN, payload);
52683
+ this.postMessage(message);
52684
+ }
52685
+ }
52686
+
52536
52687
  const Log$7 = getLogger(exports.LoggerNames.Core);
52688
+ const createProtocolV2UnlockContext = () => ({
52689
+ preflightCompleted: false,
52690
+ preflightStatusRefreshed: false,
52691
+ preflightMainPinSelected: false,
52692
+ });
52693
+ const resolvePreUnlockPinType = (method) => {
52694
+ var _a, _b;
52695
+ if (method.protocolV2PreUnlockPinType !== undefined) {
52696
+ return method.protocolV2PreUnlockPinType;
52697
+ }
52698
+ const targetsKnownHiddenWallet = method.useDevicePassphraseState &&
52699
+ ((_a = method.payload) === null || _a === void 0 ? void 0 : _a.useEmptyPassphrase) !== true &&
52700
+ typeof ((_b = method.payload) === null || _b === void 0 ? void 0 : _b.passphraseState) === 'string' &&
52701
+ method.payload.passphraseState.length > 0;
52702
+ return targetsKnownHiddenWallet ? hdTransport.DeviceSessionPinType.Any : hdTransport.DeviceSessionPinType.Main;
52703
+ };
52704
+ function runMethodWithUnlockPolicy(method, device, options = {}) {
52705
+ var _a, _b, _c;
52706
+ return __awaiter(this, void 0, void 0, function* () {
52707
+ const { context = createProtocolV2UnlockContext(), uiCoordinator, afterStatusBeforeUnlock, prepare, run: configuredRun, } = options;
52708
+ const run = configuredRun !== null && configuredRun !== void 0 ? configuredRun : (() => method.run());
52709
+ method.protocolV2UnlockContext = context;
52710
+ const shouldEmitUi = isProtocolV2UiEnabled(method);
52711
+ const shouldCoordinateUi = shouldEmitUi && uiCoordinator !== undefined;
52712
+ const requiresPreUnlock = device.isProtocolV2() &&
52713
+ (method.useDevicePassphraseState || method.unlockPolicy === 'unlock-before-run') &&
52714
+ ((_a = device.state) === null || _a === void 0 ? void 0 : _a.status.initialized) !== false &&
52715
+ !((_b = device.isBootloader) === null || _b === void 0 ? void 0 : _b.call(device)) &&
52716
+ !((_c = device.isRomloader) === null || _c === void 0 ? void 0 : _c.call(device));
52717
+ if (requiresPreUnlock && !context.preflightCompleted) {
52718
+ const status = yield refreshProtocolV2DeviceStatus(device);
52719
+ if (typeof (status === null || status === void 0 ? void 0 : status.unlocked) !== 'boolean') {
52720
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 DeviceStatus did not report an explicit unlock state.');
52721
+ }
52722
+ yield (afterStatusBeforeUnlock === null || afterStatusBeforeUnlock === void 0 ? void 0 : afterStatusBeforeUnlock());
52723
+ if (!status.unlocked) {
52724
+ const preUnlockPinType = resolvePreUnlockPinType(method);
52725
+ const unlockInteraction = shouldCoordinateUi
52726
+ ? uiCoordinator.enterUnlockInteraction(method.name)
52727
+ : undefined;
52728
+ const unlockedStatus = yield device.unlockDevice(preUnlockPinType, shouldCoordinateUi
52729
+ ? { emitUiEvent: false, interaction: unlockInteraction }
52730
+ : {
52731
+ source: 'unlock-coordinator',
52732
+ reason: 'device-locked',
52733
+ deviceOnly: true,
52734
+ method: method.name,
52735
+ });
52736
+ if ((unlockedStatus === null || unlockedStatus === void 0 ? void 0 : unlockedStatus.unlocked) !== true) {
52737
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 device remained locked after the unlock flow.');
52738
+ }
52739
+ context.preflightMainPinSelected = preUnlockPinType === hdTransport.DeviceSessionPinType.Main;
52740
+ Log$7.debug('Protocol V2 pre-unlock completed', { method: method.name });
52741
+ }
52742
+ context.preflightStatusRefreshed = true;
52743
+ context.preflightCompleted = true;
52744
+ }
52745
+ yield (prepare === null || prepare === void 0 ? void 0 : prepare());
52746
+ if (shouldCoordinateUi) {
52747
+ uiCoordinator.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
52748
+ }
52749
+ return run();
52750
+ });
52751
+ }
52752
+
52753
+ const Log$6 = getLogger(exports.LoggerNames.Core);
52537
52754
  const MIN_FILE_CHUNK_SIZE = 64;
52538
52755
  const FILE_TRANSFER_RATE_WINDOW_MS = 1000;
52539
52756
  const FILE_TRANSFER_LOG_INTERVAL_MS = 10000;
52540
52757
  const SESSION_ERROR$1 = 'session not found';
52758
+ const ASSET_FILE_WRITE_MIN_APPLICATION_VERSION = '1.0.3';
52759
+ const ASSET_FILE_WRITE_RESPONSE_TIMEOUT = 10000;
52760
+ function createProtocolV2AssetFileWriteRecovery(method) {
52761
+ var _a, _b, _c, _d;
52762
+ const { device } = method;
52763
+ const expectedSerialNo = (_b = (_a = device.state) === null || _a === void 0 ? void 0 : _a.identity) === null || _b === void 0 ? void 0 : _b.serialNo;
52764
+ const expectedDeviceType = (_d = (_c = device.state) === null || _c === void 0 ? void 0 : _c.identity) === null || _d === void 0 ? void 0 : _d.deviceType;
52765
+ return () => __awaiter(this, void 0, void 0, function* () {
52766
+ var _e, _f, _g, _h, _j, _k, _l, _m;
52767
+ if ((_e = method.abortSignal) === null || _e === void 0 ? void 0 : _e.aborted) {
52768
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled);
52769
+ }
52770
+ if (!expectedSerialNo) {
52771
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
52772
+ }
52773
+ yield device.acquire('V2', { throwOnRunPromiseError: true });
52774
+ if ((_f = method.abortSignal) === null || _f === void 0 ? void 0 : _f.aborted) {
52775
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled);
52776
+ }
52777
+ yield device.initialize();
52778
+ if (!device.isProtocolV2() ||
52779
+ ((_g = device.state) === null || _g === void 0 ? void 0 : _g.identity.serialNo) !== expectedSerialNo ||
52780
+ ((_h = device.state) === null || _h === void 0 ? void 0 : _h.identity.deviceType) !== expectedDeviceType ||
52781
+ !['normal', 'notInitialized'].includes((_k = (_j = device.state) === null || _j === void 0 ? void 0 : _j.status.mode) !== null && _k !== void 0 ? _k : '')) {
52782
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckDeviceIdError);
52783
+ }
52784
+ if ((_l = method.abortSignal) === null || _l === void 0 ? void 0 : _l.aborted) {
52785
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled);
52786
+ }
52787
+ yield runMethodWithUnlockPolicy({
52788
+ name: method.name,
52789
+ unlockPolicy: method.unlockPolicy,
52790
+ useDevicePassphraseState: method.useDevicePassphraseState,
52791
+ protocolV2PreUnlockPinType: method.protocolV2PreUnlockPinType,
52792
+ protocolV2UiMode: method.protocolV2UiMode,
52793
+ payload: method.payload,
52794
+ params: undefined,
52795
+ run: () => Promise.resolve(undefined),
52796
+ }, device, { uiCoordinator: new ProtocolV2UiInteractionCoordinator(device, method.postMessage) });
52797
+ if ((_m = method.abortSignal) === null || _m === void 0 ? void 0 : _m.aborted) {
52798
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled);
52799
+ }
52800
+ DevicePool.emitter.emit(DEVICE.CONNECT, device);
52801
+ return device.commands;
52802
+ });
52803
+ }
52804
+ function shouldPipelineAssetFileWrite(options) {
52805
+ var _a, _b, _c, _d;
52806
+ const env = DataManager.getSettings('env');
52807
+ if (env !== 'react-native' && env !== 'desktop-web-ble')
52808
+ return false;
52809
+ if (options.overwrite !== true ||
52810
+ options.append ||
52811
+ ((_a = options.offset) !== null && _a !== void 0 ? _a : 0) !== 0 ||
52812
+ ((_b = options.paceMs) !== null && _b !== void 0 ? _b : 0) > 0 ||
52813
+ options.writeWithResponse === true ||
52814
+ !/^vol1:\/(?:wallpapers\/wallpaper\.okpkg|nft\/nft-[0-9a-f]{8}-[1-9][0-9]*\.okpkg|portfolio\/portfolio\.okpkg\.pending)$/.test(options.path)) {
52815
+ return false;
52816
+ }
52817
+ const { device } = options.commands;
52818
+ const deviceType = device === null || device === void 0 ? void 0 : device.getCurrentDeviceType();
52819
+ const firmwareVersion = (_c = device === null || device === void 0 ? void 0 : device.state) === null || _c === void 0 ? void 0 : _c.versions.firmware;
52820
+ const mode = (_d = device === null || device === void 0 ? void 0 : device.state) === null || _d === void 0 ? void 0 : _d.status.mode;
52821
+ return Boolean((device === null || device === void 0 ? void 0 : device.isProtocolV2()) &&
52822
+ (deviceType === hdShared.EDeviceType.Pro2 || deviceType === hdShared.EDeviceType.Neo) &&
52823
+ (mode === 'normal' || mode === 'notInitialized') &&
52824
+ firmwareVersion &&
52825
+ semver__default["default"].valid(firmwareVersion) &&
52826
+ semver__default["default"].gte(firmwareVersion, ASSET_FILE_WRITE_MIN_APPLICATION_VERSION));
52827
+ }
52541
52828
  function formatFileTransferRate(bytesPerSecond) {
52542
52829
  return (Math.max(bytesPerSecond, 0) / 1024).toFixed(2);
52543
52830
  }
@@ -52550,11 +52837,11 @@ function getFileTransferTransport() {
52550
52837
  const env = DataManager.getSettings('env');
52551
52838
  return env && DataManager.isBleConnect(env) ? 'BLE' : String(env !== null && env !== void 0 ? env : 'unknown');
52552
52839
  }
52553
- function logFileTransferMetrics({ transport, status, path, transferredBytes, totalBytes, elapsedMs, rateBytesPerSecond, hostWriteElapsedMs, responseWaitElapsedMs, measuredAttempts, }) {
52840
+ function logFileTransferMetrics({ transport, status, path, transferredBytes, totalBytes, elapsedMs, rateBytesPerSecond, hostWriteElapsedMs, responseWaitElapsedMs, measuredAttempts, fileWriteWindow, chunkSize, retryCount, }) {
52554
52841
  const segmentedMetrics = measuredAttempts > 0
52555
52842
  ? ` hostWriteTotal=${(hostWriteElapsedMs / 1000).toFixed(2)}s responseWaitTotal=${(responseWaitElapsedMs / 1000).toFixed(2)}s measuredAttempts=${measuredAttempts}`
52556
52843
  : '';
52557
- Log$7.log(`[FileWrite] metrics transport=${transport} status=${status} path=${path} bytes=${transferredBytes}/${totalBytes} elapsed=${(elapsedMs / 1000).toFixed(2)}s speed=${formatFileTransferRate(rateBytesPerSecond)} KiB/s${segmentedMetrics}`);
52844
+ Log$6.log(`[FileWrite] metrics transport=${transport} status=${status} path=${path} bytes=${transferredBytes}/${totalBytes} elapsed=${(elapsedMs / 1000).toFixed(2)}s speed=${formatFileTransferRate(rateBytesPerSecond)} KiB/s${segmentedMetrics} window=${fileWriteWindow} chunk=${chunkSize} retry=${retryCount} averageSpeed=${formatFileTransferRate(getAverageFileTransferRate(transferredBytes, elapsedMs))} KiB/s`);
52558
52845
  }
52559
52846
  function isProtocolV2ResponseTimeout(error) {
52560
52847
  var _a, _b;
@@ -52617,7 +52904,7 @@ function getConfirmedProgress(processed, total, written, length) {
52617
52904
  return 100;
52618
52905
  }
52619
52906
  function writeProtocolV2File(options) {
52620
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
52907
+ var _a, _b, _c, _d, _e, _f;
52621
52908
  return __awaiter(this, void 0, void 0, function* () {
52622
52909
  (_a = options.throwIfAborted) === null || _a === void 0 ? void 0 : _a.call(options);
52623
52910
  const data = yield dataToUint8Array(options.data);
@@ -52630,15 +52917,21 @@ function writeProtocolV2File(options) {
52630
52917
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `FilesystemFileWrite totalSize ${totalSize} is smaller than offset + data length ${startOffset + dataLength}`);
52631
52918
  }
52632
52919
  const defaultChunkSizeLimit = getProtocolV2FileChunkLimit(options.bleChunkSizeLimit);
52920
+ const pipelineAsset = totalSize === dataLength && shouldPipelineAssetFileWrite(options);
52633
52921
  const configuredChunkSizeLimit = Number(options.chunkSizeLimit);
52634
52922
  const chunkSizeLimit = Number.isFinite(configuredChunkSizeLimit) && configuredChunkSizeLimit > 0
52635
52923
  ? Math.floor(configuredChunkSizeLimit)
52636
52924
  : defaultChunkSizeLimit;
52637
- const chunkSize = normalizeChunkSize((_b = options.chunkSize) !== null && _b !== void 0 ? _b : options.chunkLen, chunkSizeLimit);
52925
+ const chunkSize = normalizeChunkSize((_b = options.chunkSize) !== null && _b !== void 0 ? _b : options.chunkLen, pipelineAsset ? Math.min(chunkSizeLimit, hdTransport.PROTOCOL_V2_BLE_FILE_CHUNK_SIZE) : chunkSizeLimit);
52926
+ const fileWriteWindow = pipelineAsset && dataLength > chunkSize ? hdTransport.PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW : 1;
52638
52927
  let written = 0;
52639
52928
  let chunks = 0;
52640
52929
  let lastMessage;
52641
- const startTime = Date.now();
52930
+ let { commands } = options;
52931
+ let retryCount = 0;
52932
+ const maxChunkRetries = Math.max(Math.floor((_c = options.maxChunkRetries) !== null && _c !== void 0 ? _c : 0), 0);
52933
+ const canRecoverPipeline = pipelineAsset && dataLength > chunkSize && options.recoverConnection;
52934
+ let startTime = Date.now();
52642
52935
  let rateWindowStartedAt = startTime;
52643
52936
  let rateWindowStartedBytes = 0;
52644
52937
  let rateBytesPerSecond;
@@ -52648,26 +52941,151 @@ function writeProtocolV2File(options) {
52648
52941
  let hostWriteElapsedMs = 0;
52649
52942
  let responseWaitElapsedMs = 0;
52650
52943
  let measuredAttempts = 0;
52944
+ const createRequest = (position) => {
52945
+ var _a, _b, _c, _d, _e;
52946
+ const chunk = data.slice(position, Math.min(position + chunkSize, dataLength));
52947
+ const offset = startOffset + position;
52948
+ const progress = (_c = (_a = options.uiPercentage) !== null && _a !== void 0 ? _a : (_b = options.getUiPercentage) === null || _b === void 0 ? void 0 : _b.call(options, {
52949
+ offset,
52950
+ chunkLength: chunk.byteLength,
52951
+ totalSize,
52952
+ })) !== null && _c !== void 0 ? _c : getDeviceTransferProgress(offset, offset + chunk.byteLength, totalSize);
52953
+ return {
52954
+ file: { path: options.path, offset, total_size: totalSize, data: chunk },
52955
+ overwrite: position === 0 ? (_d = options.overwrite) !== null && _d !== void 0 ? _d : false : false,
52956
+ append: (_e = options.append) !== null && _e !== void 0 ? _e : false,
52957
+ ui_percentage: progress,
52958
+ };
52959
+ };
52960
+ const confirmChunk = (response, offset, chunkLength) => {
52961
+ var _a, _b, _c, _d;
52962
+ const responseType = response.type;
52963
+ if (responseType && responseType !== 'FilesystemFile') {
52964
+ const responseError = (_a = response.message) === null || _a === void 0 ? void 0 : _a.error;
52965
+ if (typeof responseError === 'string' && responseError.includes(SESSION_ERROR$1)) {
52966
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, SESSION_ERROR$1);
52967
+ }
52968
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `FilesystemFileWrite received unexpected response ${responseType}`);
52969
+ }
52970
+ (_b = options.throwIfAborted) === null || _b === void 0 ? void 0 : _b.call(options);
52971
+ lastMessage = response.message;
52972
+ const rawProcessedByte = (_c = response.message) === null || _c === void 0 ? void 0 : _c.processed_byte;
52973
+ const processedByte = Number(rawProcessedByte);
52974
+ if (rawProcessedByte !== undefined &&
52975
+ (!Number.isFinite(processedByte) ||
52976
+ processedByte <= offset ||
52977
+ processedByte > offset + chunkLength)) {
52978
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `FilesystemFileWrite invalid processed_byte ${processedByte}`);
52979
+ }
52980
+ written = rawProcessedByte === undefined ? written + chunkLength : processedByte - startOffset;
52981
+ chunks += 1;
52982
+ const now = Date.now();
52983
+ lastConfirmedAt = now;
52984
+ const elapsedMs = now - startTime;
52985
+ const transferredBytes = Math.min(written, dataLength);
52986
+ const rateWindowElapsedMs = now - rateWindowStartedAt;
52987
+ if (rateWindowElapsedMs >= FILE_TRANSFER_RATE_WINDOW_MS) {
52988
+ const rateWindowBytes = Math.max(transferredBytes - rateWindowStartedBytes, 0);
52989
+ rateBytesPerSecond = Math.round((rateWindowBytes / rateWindowElapsedMs) * 1000);
52990
+ rateWindowStartedAt = now;
52991
+ rateWindowStartedBytes = transferredBytes;
52992
+ }
52993
+ else if (rateBytesPerSecond === undefined && elapsedMs > 0) {
52994
+ rateBytesPerSecond = Math.round((transferredBytes / elapsedMs) * 1000);
52995
+ }
52996
+ (_d = options.onProgress) === null || _d === void 0 ? void 0 : _d.call(options, Object.assign({ progress: getConfirmedProgress(startOffset + written, totalSize, written, dataLength), transferredBytes, totalBytes: dataLength, rateBytesPerSecond,
52997
+ elapsedMs }, (retryCount > 0 ? { retryCount } : {})));
52998
+ const logWindowElapsedMs = now - logWindowStartedAt;
52999
+ if (logWindowElapsedMs >= FILE_TRANSFER_LOG_INTERVAL_MS && transferredBytes < dataLength) {
53000
+ const logWindowBytes = Math.max(transferredBytes - logWindowStartedBytes, 0);
53001
+ logFileTransferMetrics({
53002
+ transport,
53003
+ status: 'progress',
53004
+ path: options.path,
53005
+ transferredBytes,
53006
+ totalBytes: dataLength,
53007
+ elapsedMs,
53008
+ rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
53009
+ hostWriteElapsedMs,
53010
+ responseWaitElapsedMs,
53011
+ measuredAttempts,
53012
+ fileWriteWindow,
53013
+ chunkSize,
53014
+ retryCount,
53015
+ });
53016
+ logWindowStartedAt = now;
53017
+ logWindowStartedBytes = transferredBytes;
53018
+ }
53019
+ };
52651
53020
  const transport = getFileTransferTransport();
52652
- Log$7.log(`[FileWrite] started transport=${transport} path=${options.path} bytes=${dataLength} offset=${startOffset} chunk=${chunkSize}`);
52653
- try {
53021
+ Log$6.log(`[FileWrite] started transport=${transport} path=${options.path} bytes=${dataLength} offset=${startOffset} chunk=${chunkSize}`);
53022
+ const writeAttempt = () => __awaiter(this, void 0, void 0, function* () {
53023
+ var _g, _h, _j, _k, _l, _m, _o, _p;
53024
+ (_g = options.throwIfAborted) === null || _g === void 0 ? void 0 : _g.call(options);
53025
+ const attemptRetryCount = retryCount;
53026
+ if (fileWriteWindow === hdTransport.PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW) {
53027
+ Log$6.debug('[FileWrite] BLE asset pipeline', {
53028
+ path: options.path,
53029
+ window: fileWriteWindow,
53030
+ chunkSize,
53031
+ retryCount,
53032
+ applicationVersion: (_j = (_h = commands.device) === null || _h === void 0 ? void 0 : _h.state) === null || _j === void 0 ? void 0 : _j.versions.firmware,
53033
+ });
53034
+ let sendOffset = chunkSize;
53035
+ let interrupted = false;
53036
+ let interruption;
53037
+ const response = yield commands.typedCall('FilesystemFileWrite', 'FilesystemFile', createRequest(0), {
53038
+ timeoutMs: (_k = options.timeoutMs) !== null && _k !== void 0 ? _k : ASSET_FILE_WRITE_RESPONSE_TIMEOUT,
53039
+ writeWithResponse: false,
53040
+ fileWritePipeline: {
53041
+ next: () => {
53042
+ var _a;
53043
+ if (interrupted || attemptRetryCount !== retryCount || sendOffset >= dataLength)
53044
+ return Promise.resolve(undefined);
53045
+ try {
53046
+ (_a = options.throwIfAborted) === null || _a === void 0 ? void 0 : _a.call(options);
53047
+ const request = createRequest(sendOffset);
53048
+ sendOffset += request.file.data.byteLength;
53049
+ return Promise.resolve(request);
53050
+ }
53051
+ catch (error) {
53052
+ interrupted = true;
53053
+ interruption = error;
53054
+ return Promise.resolve(undefined);
53055
+ }
53056
+ },
53057
+ onResponse: confirmed => {
53058
+ if (interrupted || attemptRetryCount !== retryCount)
53059
+ return;
53060
+ try {
53061
+ const offset = startOffset + written;
53062
+ const length = Math.min(chunkSize, dataLength - written);
53063
+ if (Number(confirmed.message.processed_byte) !== offset + length) {
53064
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'FilesystemFileWrite pipeline did not confirm the complete chunk');
53065
+ }
53066
+ confirmChunk(confirmed, offset, length);
53067
+ }
53068
+ catch (error) {
53069
+ interrupted = true;
53070
+ interruption = error;
53071
+ }
53072
+ },
53073
+ },
53074
+ });
53075
+ (_l = options.throwIfAborted) === null || _l === void 0 ? void 0 : _l.call(options);
53076
+ if (interrupted)
53077
+ throw interruption;
53078
+ if (response.type !== 'FilesystemFile' ||
53079
+ Number(response.message.processed_byte) !== startOffset + dataLength ||
53080
+ written !== dataLength) {
53081
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'FilesystemFileWrite pipeline ended before the complete file was confirmed');
53082
+ }
53083
+ }
52654
53084
  while (written < dataLength) {
52655
- (_c = options.throwIfAborted) === null || _c === void 0 ? void 0 : _c.call(options);
52656
- const chunk = data.slice(written, Math.min(written + chunkSize, dataLength));
52657
- const offset = startOffset + written;
52658
- const progress = (_f = (_d = options.uiPercentage) !== null && _d !== void 0 ? _d : (_e = options.getUiPercentage) === null || _e === void 0 ? void 0 : _e.call(options, {
52659
- offset,
52660
- chunkLength: chunk.byteLength,
52661
- totalSize,
52662
- })) !== null && _f !== void 0 ? _f : getDeviceTransferProgress(offset, offset + chunk.byteLength, totalSize);
52663
- const request = {
52664
- file: { path: options.path, offset, total_size: totalSize, data: chunk },
52665
- overwrite: chunks === 0 ? (_g = options.overwrite) !== null && _g !== void 0 ? _g : false : false,
52666
- append: (_h = options.append) !== null && _h !== void 0 ? _h : false,
52667
- ui_percentage: progress,
52668
- };
52669
- const maxChunkRetries = Math.max(Math.floor((_j = options.maxChunkRetries) !== null && _j !== void 0 ? _j : 0), 0);
52670
- let retryCount = 0;
53085
+ (_m = options.throwIfAborted) === null || _m === void 0 ? void 0 : _m.call(options);
53086
+ const request = createRequest(written);
53087
+ const { offset, data: chunk } = request.file;
53088
+ let chunkRetryCount = 0;
52671
53089
  let response;
52672
53090
  let isWritePending = true;
52673
53091
  while (isWritePending) {
@@ -52676,7 +53094,7 @@ function writeProtocolV2File(options) {
52676
53094
  try {
52677
53095
  const callOptions = options.writeWithResponse === undefined
52678
53096
  ? {
52679
- timeoutMs: options.timeoutMs,
53097
+ timeoutMs: (_o = options.timeoutMs) !== null && _o !== void 0 ? _o : (canRecoverPipeline ? ASSET_FILE_WRITE_RESPONSE_TIMEOUT : undefined),
52680
53098
  onWriteCompleted: ({ elapsedMs }) => {
52681
53099
  currentHostWriteElapsedMs = elapsedMs;
52682
53100
  responseWaitStartedAt = Date.now();
@@ -52686,14 +53104,16 @@ function writeProtocolV2File(options) {
52686
53104
  currentHostWriteElapsedMs = elapsedMs;
52687
53105
  responseWaitStartedAt = Date.now();
52688
53106
  } });
52689
- response = yield options.commands.typedCall('FilesystemFileWrite', 'FilesystemFile', request, callOptions);
53107
+ response = yield commands.typedCall('FilesystemFileWrite', 'FilesystemFile', request, callOptions);
52690
53108
  isWritePending = false;
52691
53109
  }
52692
53110
  catch (error) {
52693
- if (retryCount >= maxChunkRetries || !isProtocolV2ResponseTimeout(error))
53111
+ if (canRecoverPipeline ||
53112
+ chunkRetryCount >= maxChunkRetries ||
53113
+ !isProtocolV2ResponseTimeout(error))
52694
53114
  throw error;
52695
- retryCount += 1;
52696
- (_k = options.throwIfAborted) === null || _k === void 0 ? void 0 : _k.call(options);
53115
+ chunkRetryCount += 1;
53116
+ (_p = options.throwIfAborted) === null || _p === void 0 ? void 0 : _p.call(options);
52697
53117
  }
52698
53118
  finally {
52699
53119
  if (currentHostWriteElapsedMs !== undefined && responseWaitStartedAt !== undefined) {
@@ -52706,92 +53126,71 @@ function writeProtocolV2File(options) {
52706
53126
  if (!response) {
52707
53127
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'FilesystemFileWrite completed without a response');
52708
53128
  }
52709
- const responseType = response.type;
52710
- if (responseType && responseType !== 'FilesystemFile') {
52711
- const responseError = (_l = response.message) === null || _l === void 0 ? void 0 : _l.error;
52712
- if (typeof responseError === 'string' && responseError.includes(SESSION_ERROR$1)) {
52713
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, SESSION_ERROR$1);
52714
- }
52715
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `FilesystemFileWrite received unexpected response ${responseType}`);
52716
- }
52717
- (_m = options.throwIfAborted) === null || _m === void 0 ? void 0 : _m.call(options);
52718
- lastMessage = response.message;
52719
- const rawProcessedByte = (_o = response.message) === null || _o === void 0 ? void 0 : _o.processed_byte;
52720
- const processedByte = Number(rawProcessedByte);
52721
- if (rawProcessedByte !== undefined &&
52722
- (!Number.isFinite(processedByte) ||
52723
- processedByte <= offset ||
52724
- processedByte > offset + chunk.byteLength)) {
52725
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `FilesystemFileWrite invalid processed_byte ${processedByte}`);
53129
+ confirmChunk(response, offset, chunk.byteLength);
53130
+ if (options.paceMs && options.paceMs > 0) {
53131
+ yield new Promise(resolve => {
53132
+ setTimeout(resolve, options.paceMs);
53133
+ });
52726
53134
  }
52727
- written =
52728
- rawProcessedByte === undefined ? written + chunk.byteLength : processedByte - startOffset;
52729
- chunks += 1;
53135
+ }
53136
+ });
53137
+ while (true) {
53138
+ try {
53139
+ yield writeAttempt();
53140
+ break;
53141
+ }
53142
+ catch (error) {
52730
53143
  const now = Date.now();
52731
- lastConfirmedAt = now;
52732
- const elapsedMs = now - startTime;
52733
- const transferredBytes = Math.min(written, dataLength);
52734
- const rateWindowElapsedMs = now - rateWindowStartedAt;
52735
- if (rateWindowElapsedMs >= FILE_TRANSFER_RATE_WINDOW_MS) {
52736
- const rateWindowBytes = Math.max(transferredBytes - rateWindowStartedBytes, 0);
52737
- rateBytesPerSecond = Math.round((rateWindowBytes / rateWindowElapsedMs) * 1000);
52738
- rateWindowStartedAt = now;
52739
- rateWindowStartedBytes = transferredBytes;
52740
- }
52741
- else if (rateBytesPerSecond === undefined && elapsedMs > 0) {
52742
- rateBytesPerSecond = Math.round((transferredBytes / elapsedMs) * 1000);
52743
- }
52744
- (_p = options.onProgress) === null || _p === void 0 ? void 0 : _p.call(options, {
52745
- progress: getConfirmedProgress(startOffset + written, totalSize, written, dataLength),
52746
- transferredBytes,
53144
+ const elapsedMs = Math.max(now - startTime, 0);
53145
+ const logWindowElapsedMs = Math.max(now - logWindowStartedAt, 0);
53146
+ const logWindowBytes = Math.max(written - logWindowStartedBytes, 0);
53147
+ logFileTransferMetrics({
53148
+ transport,
53149
+ status: 'failed',
53150
+ path: options.path,
53151
+ transferredBytes: written,
52747
53152
  totalBytes: dataLength,
52748
- rateBytesPerSecond,
52749
53153
  elapsedMs,
53154
+ rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
53155
+ hostWriteElapsedMs,
53156
+ responseWaitElapsedMs,
53157
+ measuredAttempts,
53158
+ fileWriteWindow,
53159
+ chunkSize,
53160
+ retryCount,
52750
53161
  });
52751
- const logWindowElapsedMs = now - logWindowStartedAt;
52752
- if (logWindowElapsedMs >= FILE_TRANSFER_LOG_INTERVAL_MS && transferredBytes < dataLength) {
52753
- const logWindowBytes = Math.max(transferredBytes - logWindowStartedBytes, 0);
52754
- logFileTransferMetrics({
52755
- transport,
52756
- status: 'progress',
52757
- path: options.path,
52758
- transferredBytes,
52759
- totalBytes: dataLength,
52760
- elapsedMs,
52761
- rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
52762
- hostWriteElapsedMs,
52763
- responseWaitElapsedMs,
52764
- measuredAttempts,
52765
- });
52766
- logWindowStartedAt = now;
52767
- logWindowStartedBytes = transferredBytes;
52768
- }
52769
- if (options.paceMs && options.paceMs > 0) {
52770
- yield new Promise(resolve => {
52771
- setTimeout(resolve, options.paceMs);
52772
- });
53162
+ if (!canRecoverPipeline ||
53163
+ retryCount >= maxChunkRetries ||
53164
+ !isProtocolV2ResponseTimeout(error)) {
53165
+ throw error;
52773
53166
  }
53167
+ (_d = options.throwIfAborted) === null || _d === void 0 ? void 0 : _d.call(options);
53168
+ retryCount += 1;
53169
+ Log$6.log(`[FileWrite] retry=${retryCount}/${maxChunkRetries} restarting window=${fileWriteWindow} after reconnect path=${options.path}`);
53170
+ (_e = options.onProgress) === null || _e === void 0 ? void 0 : _e.call(options, {
53171
+ progress: 0,
53172
+ transferredBytes: 0,
53173
+ totalBytes: dataLength,
53174
+ elapsedMs: 0,
53175
+ retryCount,
53176
+ });
53177
+ commands = yield canRecoverPipeline();
53178
+ (_f = options.throwIfAborted) === null || _f === void 0 ? void 0 : _f.call(options);
53179
+ written = 0;
53180
+ chunks = 0;
53181
+ lastMessage = undefined;
53182
+ startTime = Date.now();
53183
+ lastConfirmedAt = startTime;
53184
+ rateWindowStartedAt = startTime;
53185
+ rateWindowStartedBytes = 0;
53186
+ rateBytesPerSecond = undefined;
53187
+ logWindowStartedAt = startTime;
53188
+ logWindowStartedBytes = 0;
53189
+ hostWriteElapsedMs = 0;
53190
+ responseWaitElapsedMs = 0;
53191
+ measuredAttempts = 0;
52774
53192
  }
52775
53193
  }
52776
- catch (error) {
52777
- const now = Date.now();
52778
- const elapsedMs = Math.max(now - startTime, 0);
52779
- const logWindowElapsedMs = Math.max(now - logWindowStartedAt, 0);
52780
- const logWindowBytes = Math.max(written - logWindowStartedBytes, 0);
52781
- logFileTransferMetrics({
52782
- transport,
52783
- status: 'failed',
52784
- path: options.path,
52785
- transferredBytes: written,
52786
- totalBytes: dataLength,
52787
- elapsedMs,
52788
- rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
52789
- hostWriteElapsedMs,
52790
- responseWaitElapsedMs,
52791
- measuredAttempts,
52792
- });
52793
- throw error;
52794
- }
52795
53194
  const elapsedMs = Math.max(lastConfirmedAt - startTime, 0);
52796
53195
  const logWindowElapsedMs = Math.max(lastConfirmedAt - logWindowStartedAt, 0);
52797
53196
  const logWindowBytes = Math.max(written - logWindowStartedBytes, 0);
@@ -52806,12 +53205,16 @@ function writeProtocolV2File(options) {
52806
53205
  hostWriteElapsedMs,
52807
53206
  responseWaitElapsedMs,
52808
53207
  measuredAttempts,
53208
+ fileWriteWindow,
53209
+ chunkSize,
53210
+ retryCount,
52809
53211
  });
52810
53212
  return Object.assign(Object.assign({}, lastMessage), { path: options.path, offset: startOffset, total_size: totalSize, processed_byte: startOffset + written, chunks });
52811
53213
  });
52812
53214
  }
52813
53215
 
52814
- const Log$6 = getLogger(exports.LoggerNames.Method);
53216
+ const Log$5 = getLogger(exports.LoggerNames.Method);
53217
+ const TransferLog = getLogger(exports.LoggerNames.Core);
52815
53218
  const SESSION_ERROR = 'session not found';
52816
53219
  const PROTOCOL_V2_BOOTLOADER_RECONNECT_TIMEOUT = 90 * 1000;
52817
53220
  const PROTOCOL_V2_FINAL_RECONNECT_TIMEOUT = 3 * 60 * 1000;
@@ -52836,6 +53239,7 @@ const PROTOCOL_V2_FILE_TRANSFER_RETRY_COUNT = 3;
52836
53239
  const PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT = 2;
52837
53240
  const PROTOCOL_V2_BLE_FILE_WRITE_WINDOW_MIN_BOOTLOADER_VERSION = '1.0.3';
52838
53241
  const PROTOCOL_V2_TRANSFER_PROGRESS_HEARTBEAT_MS = 1000;
53242
+ const PROTOCOL_V2_TRANSFER_LOG_INTERVAL_MS = 10000;
52839
53243
  const PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE = 'FirmwareInstallStatusConflict';
52840
53244
  const PROTOCOL_V2_INSTALL_FAILED_CODE = 'FirmwareInstallFailed';
52841
53245
  const PROTOCOL_V2_INSTALL_TIMEOUT_CODE = 'FirmwareInstallTimeout';
@@ -53143,7 +53547,6 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53143
53547
  this.protocolV2InstallTerminalSuccessObserved = false;
53144
53548
  this.protocolV2LastTransferProgressAt = 0;
53145
53549
  this.protocolV2LastTransferredBytes = 0;
53146
- this.protocolV2FileWriteWindowDisabled = false;
53147
53550
  }
53148
53551
  getSupportedProtocols() {
53149
53552
  return ['V2'];
@@ -53316,7 +53719,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53316
53719
  }
53317
53720
  run() {
53318
53721
  return __awaiter(this, void 0, void 0, function* () {
53319
- Log$6.debug('FirmwareUpdateV4 strategy: Protocol V2');
53722
+ Log$5.debug('FirmwareUpdateV4 strategy: Protocol V2');
53320
53723
  return this.runProtocolV2();
53321
53724
  });
53322
53725
  }
@@ -53749,7 +54152,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
53749
54152
  if (!observedVersion) {
53750
54153
  const hasCompleteTargetEvidence = targetId !== undefined && this.protocolV2CompletedTargetIds.has(targetId);
53751
54154
  if (hasCompleteTargetEvidence) {
53752
- Log$6.warn(`Protocol V2 target ${target} has no observable final version; install completion is authoritative`);
54155
+ Log$5.warn(`Protocol V2 target ${target} has no observable final version; install completion is authoritative`);
53753
54156
  }
53754
54157
  else {
53755
54158
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareVerificationFailed, `Protocol V2 target ${target} has no observable final version after status fallback`);
@@ -54061,8 +54464,8 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54061
54464
  const chunkSize = this.getProtocolV2FirmwareChunkSize('read');
54062
54465
  const chunks = [];
54063
54466
  let offset = 0;
54064
- while (offset < PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE) {
54065
- const readLen = Math.min(chunkSize, PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE - offset);
54467
+ while (offset < PROTOCOL_V2_RESOURCE_PACKAGE_COMPARE_SIZE) {
54468
+ const readLen = Math.min(chunkSize, PROTOCOL_V2_RESOURCE_PACKAGE_COMPARE_SIZE - offset);
54066
54469
  const res = yield typedCall('FilesystemFileRead', 'FilesystemFile', {
54067
54470
  file: {
54068
54471
  path: filePath,
@@ -54073,7 +54476,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54073
54476
  ui_percentage: undefined,
54074
54477
  });
54075
54478
  const data = toProtocolV2Bytes((_d = res.message) === null || _d === void 0 ? void 0 : _d.data);
54076
- if (data.byteLength === 0)
54479
+ if (data.byteLength === 0 || data.byteLength > readLen)
54077
54480
  return null;
54078
54481
  chunks.push(data);
54079
54482
  offset += data.byteLength;
@@ -54119,7 +54522,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54119
54522
  catch (error) {
54120
54523
  if (isProtocolV2ResourceHeaderLinkError(error))
54121
54524
  throw error;
54122
- Log$6.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
54525
+ Log$5.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check failed: `, error);
54123
54526
  return false;
54124
54527
  }
54125
54528
  });
@@ -54133,11 +54536,11 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54133
54536
  catch (error) {
54134
54537
  this.throwIfAborted();
54135
54538
  if (recovery.remaining <= 0) {
54136
- Log$6.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check lost the link; reconnect budget used up: `, getProtocolV2UnknownErrorText(error));
54539
+ Log$5.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check lost the link; reconnect budget used up: `, getProtocolV2UnknownErrorText(error));
54137
54540
  return false;
54138
54541
  }
54139
54542
  recovery.remaining -= 1;
54140
- Log$6.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check lost the link; reconnecting: `, getProtocolV2UnknownErrorText(error));
54543
+ Log$5.log(`[FirmwareUpdateV4] RESC bundle ${bundle.name} header check lost the link; reconnecting: `, getProtocolV2UnknownErrorText(error));
54141
54544
  }
54142
54545
  yield this.recoverProtocolV2FileTransfer();
54143
54546
  this.throwIfAborted();
@@ -54165,11 +54568,15 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54165
54568
  return ((_a = this.device.features) === null || _a === void 0 ? void 0 : _a.mode) === 'romloader';
54166
54569
  }
54167
54570
  rebootProtocolV2ToBootloader() {
54571
+ var _a;
54168
54572
  return __awaiter(this, void 0, void 0, function* () {
54169
54573
  try {
54170
54574
  this.postTipMessage(exports.FirmwareUpdateTipMessage.AutoRebootToBootloader);
54171
54575
  yield this.protocolV2Reboot(hdTransport.DeviceRebootType.Bootloader);
54172
54576
  yield hdShared.wait(1000);
54577
+ if (this.isBleReconnect()) {
54578
+ yield ((_a = this.device.deviceConnector) === null || _a === void 0 ? void 0 : _a.disconnect(this.device.originalDescriptor.id));
54579
+ }
54173
54580
  yield this.waitForProtocolV2BootloaderMode();
54174
54581
  this.protocolV2ExecutionInLoader = true;
54175
54582
  this.postTipMessage(exports.FirmwareUpdateTipMessage.GoToBootloaderSuccess);
@@ -54179,7 +54586,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54179
54586
  if (error instanceof hdShared.HardwareError) {
54180
54587
  throw error;
54181
54588
  }
54182
- Log$6.log('Protocol V2 auto go to bootloader mode failed: ', error);
54589
+ Log$5.log('Protocol V2 auto go to bootloader mode failed: ', error);
54183
54590
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateAutoEnterBootFailure);
54184
54591
  }
54185
54592
  });
@@ -54187,12 +54594,12 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54187
54594
  enterProtocolV2BootloaderMode() {
54188
54595
  return __awaiter(this, void 0, void 0, function* () {
54189
54596
  if (this.isProtocolV2RomloaderMode()) {
54190
- Log$6.debug('Protocol V2 device is in romloader mode; start firmware update directly');
54597
+ Log$5.debug('Protocol V2 device is in romloader mode; start firmware update directly');
54191
54598
  this.protocolV2ExecutionInLoader = true;
54192
54599
  return false;
54193
54600
  }
54194
54601
  if (this.isProtocolV2BootloaderMode()) {
54195
- Log$6.debug('Protocol V2 device is already in bootloader mode, skip reboot');
54602
+ Log$5.debug('Protocol V2 device is already in bootloader mode, skip reboot');
54196
54603
  this.protocolV2ExecutionInLoader = true;
54197
54604
  this.postTipMessage(exports.FirmwareUpdateTipMessage.GoToBootloaderSuccess);
54198
54605
  return false;
@@ -54208,12 +54615,17 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54208
54615
  while (Date.now() - startTime < timeout) {
54209
54616
  try {
54210
54617
  if (shouldReconnect) {
54211
- yield this.reconnectProtocolV2Device();
54618
+ if (this.isBleReconnect()) {
54619
+ yield this.reconnectProtocolV2Device({ skipProtocolProbe: true });
54620
+ }
54621
+ else {
54622
+ yield this.reconnectProtocolV2Device();
54623
+ }
54212
54624
  shouldReconnect = false;
54213
54625
  }
54214
54626
  const deviceInfo = yield requestProtocolV2DeviceInfo({
54215
54627
  commands: this.device.getCommands(),
54216
- timeoutMs: this.getProtocolV2DeviceInfoTimeout(),
54628
+ timeoutMs: Math.min(this.getProtocolV2DeviceInfoTimeout(), PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT),
54217
54629
  });
54218
54630
  this.assertProtocolV2DeviceInfoIdentity(deviceInfo);
54219
54631
  const features = yield this.device.probeProtocolV2RuntimeState(deviceInfo, PROTOCOL_V2_SHORT_RESPONSE_TIMEOUT);
@@ -54228,7 +54640,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54228
54640
  }
54229
54641
  shouldReconnect = true;
54230
54642
  lastError = error;
54231
- Log$6.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
54643
+ Log$5.log('Protocol V2 bootloader mode not ready, polling reconnect: ', error);
54232
54644
  }
54233
54645
  yield hdShared.wait(retryInterval);
54234
54646
  }
@@ -54325,7 +54737,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54325
54737
  const headerRecovery = { remaining: PROTOCOL_V2_RESOURCE_HEADER_RECOVERY_LIMIT };
54326
54738
  for (const resource of resourceSources) {
54327
54739
  if (yield this.isProtocolV2ResourceBundleUpToDateWithRecovery(resource, headerRecovery)) {
54328
- Log$6.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
54740
+ Log$5.log(`[FirmwareUpdateV4] skip RESC bundle ${resource.name}; already up to date`);
54329
54741
  }
54330
54742
  else {
54331
54743
  resourcesToSync.push(resource);
@@ -54377,7 +54789,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54377
54789
  rateBytesPerSecond,
54378
54790
  elapsedMs,
54379
54791
  });
54380
- Log$6.debug('[FirmwareUpdateV4] Protocol V2 transfer completed', {
54792
+ Log$5.debug('[FirmwareUpdateV4] Protocol V2 transfer completed', {
54381
54793
  files: resourcesToSync.length + installSources.length,
54382
54794
  totalBytes: totalSize,
54383
54795
  elapsedMs,
@@ -54397,7 +54809,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54397
54809
  yield this.protocolV2StartFirmwareUpdate({ targets });
54398
54810
  yield hdShared.wait(PROTOCOL_V2_INSTALL_STATUS_INITIAL_DELAY);
54399
54811
  yield this.waitForProtocolV2FirmwareUpdateComplete(targets, true);
54400
- Log$6.debug('[FirmwareUpdateV4] Protocol V2 install completed', {
54812
+ Log$5.debug('[FirmwareUpdateV4] Protocol V2 install completed', {
54401
54813
  targets: targets.length,
54402
54814
  elapsedMs: getProtocolV2ElapsedMs(installStartedAt),
54403
54815
  });
@@ -54436,10 +54848,37 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54436
54848
  this.throwIfAborted();
54437
54849
  const attemptStartedAt = Date.now();
54438
54850
  let windowed = false;
54851
+ let chunkSize = 0;
54852
+ let confirmedBytes = 0;
54853
+ let lastMetricsAt = attemptStartedAt;
54854
+ const logTransfer = (status) => {
54855
+ const elapsedMs = getProtocolV2ElapsedMs(attemptStartedAt);
54856
+ TransferLog.log('[FirmwareUpdateV4] FileWrite metrics', {
54857
+ status,
54858
+ path: filePath,
54859
+ fileWriteWindow: windowed ? hdTransport.PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW : 1,
54860
+ chunkSize,
54861
+ attempt,
54862
+ retryCount: attempt - 1,
54863
+ transferredBytes: confirmedBytes,
54864
+ totalBytes: source.size,
54865
+ elapsedMs,
54866
+ rateBytesPerSecond: getProtocolV2TransferRate(confirmedBytes, elapsedMs),
54867
+ });
54868
+ };
54869
+ const reportAttemptChunk = (chunkEnd) => {
54870
+ confirmedBytes = chunkEnd;
54871
+ reportConfirmedChunk(chunkEnd);
54872
+ const now = Date.now();
54873
+ if (now - lastMetricsAt >= PROTOCOL_V2_TRANSFER_LOG_INTERVAL_MS && chunkEnd < source.size) {
54874
+ logTransfer('progress');
54875
+ lastMetricsAt = now;
54876
+ }
54877
+ };
54439
54878
  try {
54440
- windowed =
54441
- !this.protocolV2FileWriteWindowDisabled && this.shouldUseProtocolV2BleFileWriteWindow();
54442
- const chunkSize = this.getProtocolV2FirmwareChunkSize('write', filePath, windowed);
54879
+ windowed = this.shouldUseProtocolV2BleFileWriteWindow();
54880
+ chunkSize = this.getProtocolV2FirmwareChunkSize('write', filePath, windowed);
54881
+ logTransfer('started');
54443
54882
  if (windowed) {
54444
54883
  yield this.writeProtocolV2SourceWindowed({
54445
54884
  source,
@@ -54447,7 +54886,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54447
54886
  chunkSize,
54448
54887
  processedSize,
54449
54888
  totalSize,
54450
- onChunkConfirmed: reportConfirmedChunk,
54889
+ onChunkConfirmed: reportAttemptChunk,
54451
54890
  });
54452
54891
  }
54453
54892
  else {
@@ -54464,24 +54903,19 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54464
54903
  if (!Number.isFinite(nextOffset) || nextOffset !== chunkEnd) {
54465
54904
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.EmmcFileWriteFirmwareError, `invalid processed_byte ${rawProcessedByte} for offset ${sourceOffset}`);
54466
54905
  }
54467
- reportConfirmedChunk(chunkEnd);
54906
+ reportAttemptChunk(chunkEnd);
54468
54907
  return length;
54469
54908
  }),
54470
54909
  });
54471
54910
  }
54472
- Log$6.debug('[FirmwareUpdateV4] Protocol V2 file transferred', {
54473
- path: filePath,
54474
- bytes: source.size,
54475
- elapsedMs: getProtocolV2ElapsedMs(attemptStartedAt),
54476
- attempt,
54477
- fileWriteWindow: windowed ? hdTransport.PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW : 1,
54478
- });
54911
+ logTransfer('completed');
54479
54912
  return processedSize + source.size;
54480
54913
  }
54481
54914
  catch (error) {
54482
54915
  this.throwIfAborted();
54483
54916
  lastError = error;
54484
- Log$6.debug('[FirmwareUpdateV4] Protocol V2 file transfer attempt failed', {
54917
+ logTransfer('failed');
54918
+ TransferLog.debug('[FirmwareUpdateV4] Protocol V2 file transfer attempt failed', {
54485
54919
  path: filePath,
54486
54920
  attempt,
54487
54921
  fileWriteWindow: windowed ? hdTransport.PROTOCOL_V2_FILE_WRITE_PIPELINE_WINDOW : 1,
@@ -54523,7 +54957,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54523
54957
  }
54524
54958
  assertProtocolV2TargetStatus(statusTargets, expectedTargetIds, expectedPaths, liveTargetIds) {
54525
54959
  const resolvedExpectedPaths = expectedPaths !== null && expectedPaths !== void 0 ? expectedPaths : new Map();
54526
- Log$6.log(`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`);
54960
+ Log$5.log(`[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`);
54527
54961
  const failedTarget = statusTargets.find(target => {
54528
54962
  var _a;
54529
54963
  const targetId = (_a = normalizeProtocolV2TargetId(target.target_id)) !== null && _a !== void 0 ? _a : -1;
@@ -54538,7 +54972,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54538
54972
  payloadVersion: failedTarget.payload_version,
54539
54973
  path: failedTarget.path,
54540
54974
  });
54541
- Log$6.error(`[FirmwareUpdateV4] firmware install failed target=${failedTargetDetails}`);
54975
+ Log$5.error(`[FirmwareUpdateV4] firmware install failed target=${failedTargetDetails}`);
54542
54976
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareError, 'Protocol V2 firmware install failed', {
54543
54977
  firmwareUpdateCode: PROTOCOL_V2_INSTALL_FAILED_CODE,
54544
54978
  });
@@ -54552,7 +54986,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54552
54986
  !liveTargetIds.has(targetId));
54553
54987
  });
54554
54988
  if (staleFailedTargets.length > 0) {
54555
- Log$6.log(`[FirmwareUpdateV4] ignoring stale failed firmware status until those targets start: ${JSON.stringify(staleFailedTargets.map(target => ({
54989
+ Log$5.log(`[FirmwareUpdateV4] ignoring stale failed firmware status until those targets start: ${JSON.stringify(staleFailedTargets.map(target => ({
54556
54990
  targetId: target.target_id,
54557
54991
  status: target.status,
54558
54992
  path: target.path,
@@ -54571,7 +55005,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54571
55005
  if (targetId === undefined ||
54572
55006
  seenTargetIds.has(targetId) ||
54573
55007
  pathConflictsWithCurrentInstall) {
54574
- Log$6.error(`[FirmwareUpdateV4] install status conflicts with target=${target.target_id}`);
55008
+ Log$5.error(`[FirmwareUpdateV4] install status conflicts with target=${target.target_id}`);
54575
55009
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareError, 'Protocol V2 firmware install status conflict', {
54576
55010
  firmwareUpdateCode: PROTOCOL_V2_INSTALL_STATUS_CONFLICT_CODE,
54577
55011
  });
@@ -54879,7 +55313,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54879
55313
  if (isNormalMode &&
54880
55314
  (installEvidenceObserved ||
54881
55315
  this.hasProtocolV2InstallVersionChanged(expectedTargetIds, currentDeviceInfo))) {
54882
- Log$6.log('[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete');
55316
+ Log$5.log('[FirmwareUpdateV4] empty firmware status after confirmed App reboot; update complete');
54883
55317
  this.recordProtocolV2AuthoritativeInstallCompletion(expectedTargetIds);
54884
55318
  this.postProgressMessage(100, 'installingFirmware');
54885
55319
  return;
@@ -54913,7 +55347,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54913
55347
  if (isNormalMode &&
54914
55348
  (installEvidenceObserved ||
54915
55349
  this.hasProtocolV2InstallVersionChanged(expectedTargetIds, deviceInfo))) {
54916
- Log$6.log('[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot');
55350
+ Log$5.log('[FirmwareUpdateV4] firmware status endpoint unavailable after confirmed App reboot');
54917
55351
  this.recordProtocolV2AuthoritativeInstallCompletion(expectedTargetIds);
54918
55352
  this.postProgressMessage(100, 'installingFirmware');
54919
55353
  return;
@@ -54931,7 +55365,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54931
55365
  deviceInfo = undefined;
54932
55366
  bleInstallLinkReady = false;
54933
55367
  lastError = error;
54934
- Log$6.log('[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ', error);
55368
+ Log$5.log('[FirmwareUpdateV4] DeviceFirmwareUpdateStatusGet unavailable during install: ', error);
54935
55369
  }
54936
55370
  }
54937
55371
  }
@@ -54951,13 +55385,13 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54951
55385
  shouldReconnect = true;
54952
55386
  deviceInfo = undefined;
54953
55387
  bleInstallLinkReady = false;
54954
- Log$6.log('Protocol V2 firmware install device readiness probe failed: ', error);
55388
+ Log$5.log('Protocol V2 firmware install device readiness probe failed: ', error);
54955
55389
  }
54956
55390
  if (isFinalStatusCheck)
54957
55391
  break;
54958
55392
  yield hdShared.wait(1000);
54959
55393
  }
54960
- Log$6.error(`[FirmwareUpdateV4] install timed out after ${PROTOCOL_V2_INSTALL_TIMEOUT / 1000}s: ${this.normalizeErrorMessage(lastError)}`);
55394
+ Log$5.error(`[FirmwareUpdateV4] install timed out after ${PROTOCOL_V2_INSTALL_TIMEOUT / 1000}s: ${this.normalizeErrorMessage(lastError)}`);
54961
55395
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareError, 'Protocol V2 firmware install timed out', {
54962
55396
  firmwareUpdateCode: PROTOCOL_V2_INSTALL_TIMEOUT_CODE,
54963
55397
  });
@@ -54969,12 +55403,12 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
54969
55403
  const deviceInfo = yield this.verifyProtocolV2ReconnectIdentity();
54970
55404
  try {
54971
55405
  if (yield this.probeProtocolV2NormalMode(deviceInfo)) {
54972
- Log$6.log('[FirmwareUpdateV4] device already returned to App mode; skip Normal reboot');
55406
+ Log$5.log('[FirmwareUpdateV4] device already returned to App mode; skip Normal reboot');
54973
55407
  return;
54974
55408
  }
54975
55409
  }
54976
55410
  catch (error) {
54977
- Log$6.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
55411
+ Log$5.log('[FirmwareUpdateV4] unable to confirm App mode before Normal reboot: ', error);
54978
55412
  }
54979
55413
  yield this.protocolV2Reboot(hdTransport.DeviceRebootType.Normal);
54980
55414
  this.protocolV2ExecutionInLoader = false;
@@ -55002,7 +55436,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
55002
55436
  const firmwareVersion = getDeviceFirmwareVersion(features).join('.');
55003
55437
  this.protocolV2LatestFinalFeatures = features;
55004
55438
  if (firmwareVersion === '0.0.0') {
55005
- Log$6.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.');
55439
+ 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.');
55006
55440
  }
55007
55441
  return {
55008
55442
  bootloaderVersion,
@@ -55053,7 +55487,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
55053
55487
  }
55054
55488
  shouldReconnect = true;
55055
55489
  lastError = error;
55056
- Log$6.log('Protocol V2 normal mode not ready, polling Ping: ', error);
55490
+ Log$5.log('Protocol V2 normal mode not ready, polling Ping: ', error);
55057
55491
  }
55058
55492
  yield hdShared.wait(1000);
55059
55493
  }
@@ -55117,7 +55551,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
55117
55551
  if (!reconnectDevice) {
55118
55552
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
55119
55553
  }
55120
- Log$6.debug('Protocol V2 firmware reconnect using matched device:', reconnectDevice.getConnectId());
55554
+ Log$5.debug('Protocol V2 firmware reconnect using matched device:', reconnectDevice.getConnectId());
55121
55555
  this.device.updateFromCache(reconnectDevice);
55122
55556
  yield this.ensureProtocolV2DeviceAcquired();
55123
55557
  this.device.commands.disposed = false;
@@ -55178,7 +55612,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
55178
55612
  }
55179
55613
  const bootloaderVersion = getDeviceBootloaderVersion(this.device.features).join('.');
55180
55614
  const enabled = supportsProtocolV2BleFileWriteWindow(bootloaderVersion, minBootloaderVersion);
55181
- Log$6.debug('[FirmwareUpdateV4] BLE FileWrite window', {
55615
+ TransferLog.debug('[FirmwareUpdateV4] BLE FileWrite window', {
55182
55616
  enabled,
55183
55617
  bootloaderVersion,
55184
55618
  bootloaderBuildId: (_b = (_a = this.device.features) === null || _a === void 0 ? void 0 : _a.verify) === null || _b === void 0 ? void 0 : _b.bootloaderBuildId,
@@ -55228,17 +55662,12 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
55228
55662
  onChunkConfirmed(Number(confirmed.message.processed_byte));
55229
55663
  },
55230
55664
  },
55231
- })
55232
- .catch((error) => {
55233
- this.protocolV2FileWriteWindowDisabled = true;
55234
- throw error;
55235
55665
  });
55236
55666
  this.throwIfAborted();
55237
55667
  if (sourceError)
55238
55668
  throw sourceError;
55239
55669
  const rawProcessedByte = response.message.processed_byte;
55240
55670
  if (Number(rawProcessedByte) !== source.size) {
55241
- this.protocolV2FileWriteWindowDisabled = true;
55242
55671
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.EmmcFileWriteFirmwareError, `invalid processed_byte ${rawProcessedByte} for file size ${source.size}`);
55243
55672
  }
55244
55673
  });
@@ -55294,17 +55723,17 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
55294
55723
  return;
55295
55724
  this.protocolV2InstallTerminalSuccessObserved = true;
55296
55725
  this.device.clearCancelableAction();
55297
- Log$6.log('[FirmwareUpdateV4] firmware install completed by device response');
55726
+ Log$5.log('[FirmwareUpdateV4] firmware install completed by device response');
55298
55727
  },
55299
55728
  });
55300
- Log$6.log('[FirmwareUpdateV4] firmware install request written; continue with status polling');
55729
+ Log$5.log('[FirmwareUpdateV4] firmware install request written; continue with status polling');
55301
55730
  }
55302
55731
  catch (error) {
55303
55732
  this.throwIfAborted();
55304
55733
  if (!isProtocolV2DeviceDisconnectedError(error)) {
55305
55734
  throw error;
55306
55735
  }
55307
- Log$6.log('[FirmwareUpdateV4] transport released while writing install request; continue status polling');
55736
+ Log$5.log('[FirmwareUpdateV4] transport released while writing install request; continue status polling');
55308
55737
  this.protocolV2InstallNeedsReconnect = true;
55309
55738
  this.protocolV2InstallDisconnectObserved = !isProtocolV2InstallResponseTimeout(error);
55310
55739
  }
@@ -55339,7 +55768,7 @@ class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod {
55339
55768
  }
55340
55769
  }
55341
55770
 
55342
- const Log$5 = getLogger(exports.LoggerNames.Method);
55771
+ const Log$4 = getLogger(exports.LoggerNames.Method);
55343
55772
  class PromptWebDeviceAccess extends BaseMethod {
55344
55773
  init() {
55345
55774
  this.useDevice = false;
@@ -55394,7 +55823,7 @@ class PromptWebDeviceAccess extends BaseMethod {
55394
55823
  return { device: null };
55395
55824
  }
55396
55825
  catch (error) {
55397
- Log$5.debug(error);
55826
+ Log$4.debug(error);
55398
55827
  return Promise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Please select the device to connect'));
55399
55828
  }
55400
55829
  });
@@ -55765,7 +56194,7 @@ const DEVICE_SETTINGS_SET_MESSAGE_TYPE = 60412;
55765
56194
  const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE$2 = 60805;
55766
56195
  const FILESYSTEM_DIR_MAKE_MESSAGE_TYPE = 60809;
55767
56196
  const WALLPAPER_PACKAGE_BLE_CHUNK_SIZE = 1960;
55768
- const Log$4 = getLogger(exports.LoggerNames.Method);
56197
+ const Log$3 = getLogger(exports.LoggerNames.Method);
55769
56198
  function normalizeFileName(fileName, data) {
55770
56199
  if (fileName !== undefined && (!fileName || !SAFE_FILE_NAME.test(fileName))) {
55771
56200
  throw invalidParameter$1('Parameter [fileName] may only contain letters, numbers, underscores, hyphens and an optional .bin suffix.');
@@ -55848,6 +56277,7 @@ class DeviceUploadWallpaper extends BaseMethod {
55848
56277
  chunkSize: this.params.chunkSize,
55849
56278
  bleChunkSizeLimit,
55850
56279
  maxChunkRetries: 3,
56280
+ recoverConnection: createProtocolV2AssetFileWriteRecovery(this),
55851
56281
  overwrite: true,
55852
56282
  append: false,
55853
56283
  throwIfAborted: () => this.throwIfAborted(),
@@ -55875,6 +56305,15 @@ class DeviceUploadWallpaper extends BaseMethod {
55875
56305
  if (useHostAssetPackage)
55876
56306
  this.path = WALLPAPER_PACKAGE_PATH;
55877
56307
  yield this.upload(this.path, data, useHostAssetPackage ? WALLPAPER_PACKAGE_BLE_CHUNK_SIZE : undefined);
56308
+ this.throwIfAborted();
56309
+ new ProtocolV2UiInteractionCoordinator(this.device, this.postMessage).enterMethodInteraction({
56310
+ request: 'button',
56311
+ source: 'method-lifecycle',
56312
+ reason: 'device-management',
56313
+ completion: 'operation-completed',
56314
+ deviceOnly: true,
56315
+ operation: this.name,
56316
+ });
55878
56317
  const response = yield this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
55879
56318
  settings: { wallpaper_path: this.path },
55880
56319
  });
@@ -55882,7 +56321,7 @@ class DeviceUploadWallpaper extends BaseMethod {
55882
56321
  yield this.device.refreshProtocolV2SettingsAfterMutation();
55883
56322
  }
55884
56323
  catch (error) {
55885
- Log$4.warn('Protocol V2 wallpaper settings refresh failed after apply', {
56324
+ Log$3.warn('Protocol V2 wallpaper settings refresh failed after apply', {
55886
56325
  error: error instanceof Error ? error.message : String(error),
55887
56326
  });
55888
56327
  }
@@ -56039,6 +56478,8 @@ class DeviceUploadNft extends BaseMethod {
56039
56478
  chunkSize: this.params.chunkSize,
56040
56479
  timeoutMs: this.params.timeoutMs,
56041
56480
  paceMs: this.params.paceMs,
56481
+ maxChunkRetries: 3,
56482
+ recoverConnection: createProtocolV2AssetFileWriteRecovery(this),
56042
56483
  overwrite: true,
56043
56484
  append: false,
56044
56485
  throwIfAborted: () => this.throwIfAborted(),
@@ -56093,25 +56534,13 @@ class FileWrite extends BaseMethod {
56093
56534
  }
56094
56535
  run() {
56095
56536
  return __awaiter(this, void 0, void 0, function* () {
56096
- return writeProtocolV2File({
56097
- commands: this.device.commands,
56098
- path: this.params.path,
56099
- data: this.params.data,
56100
- offset: this.params.offset,
56101
- totalSize: this.params.totalSize,
56102
- chunkSize: this.params.chunkSize,
56103
- chunkLen: this.params.chunkLen,
56104
- overwrite: this.params.overwrite,
56105
- append: this.params.append,
56106
- uiPercentage: this.params.uiPercentage,
56107
- timeoutMs: this.params.timeoutMs === undefined ? undefined : Number(this.params.timeoutMs),
56108
- throwIfAborted: () => this.throwIfAborted(),
56109
- onProgress: payload => {
56537
+ return writeProtocolV2File(Object.assign(Object.assign({ commands: this.device.commands, path: this.params.path, data: this.params.data, offset: this.params.offset, totalSize: this.params.totalSize, chunkSize: this.params.chunkSize, chunkLen: this.params.chunkLen, overwrite: this.params.overwrite, append: this.params.append, uiPercentage: this.params.uiPercentage, timeoutMs: this.params.timeoutMs === undefined ? undefined : Number(this.params.timeoutMs) }, (this.name === 'uploadPortfolio'
56538
+ ? { maxChunkRetries: 3, recoverConnection: createProtocolV2AssetFileWriteRecovery(this) }
56539
+ : {})), { throwIfAborted: () => this.throwIfAborted(), onProgress: payload => {
56110
56540
  if (this.params.emitProgress && typeof this.postMessage === 'function') {
56111
56541
  this.postMessage(createUiMessage(UI_REQUEST.DEVICE_PROGRESS, payload));
56112
56542
  }
56113
- },
56114
- });
56543
+ } }));
56115
56544
  });
56116
56545
  }
56117
56546
  }
@@ -56207,201 +56636,6 @@ class CipherKeyValue extends BaseMethod {
56207
56636
  }
56208
56637
  }
56209
56638
 
56210
- const isProtocolV2UiEnabled = (method) => method.protocolV2UiMode !== 'none';
56211
- const getDisplayState = (value) => {
56212
- const visited = new Set();
56213
- let seen = false;
56214
- let enabled = false;
56215
- const visit = (current) => {
56216
- if (current == null || typeof current !== 'object')
56217
- return;
56218
- if (visited.has(current))
56219
- return;
56220
- visited.add(current);
56221
- if (Array.isArray(current)) {
56222
- current.forEach(visit);
56223
- return;
56224
- }
56225
- Object.entries(current).forEach(([key, item]) => {
56226
- if ((key === 'show_display' || key === 'showOnOneKey') && typeof item === 'boolean') {
56227
- seen = true;
56228
- enabled || (enabled = item);
56229
- return;
56230
- }
56231
- visit(item);
56232
- });
56233
- };
56234
- visit(value);
56235
- return { seen, enabled };
56236
- };
56237
- const createMethodInteraction = (reason, operation) => ({
56238
- request: 'button',
56239
- source: 'method-lifecycle',
56240
- reason,
56241
- completion: 'operation-completed',
56242
- deviceOnly: true,
56243
- operation,
56244
- });
56245
- const hasCipherConfirmation = (value) => {
56246
- if (Array.isArray(value))
56247
- return value.some(hasCipherConfirmation);
56248
- if (value == null || typeof value !== 'object')
56249
- return false;
56250
- const params = value;
56251
- return params.encrypt === true ? params.ask_on_encrypt === true : params.ask_on_decrypt === true;
56252
- };
56253
- const resolveProtocolV2UiInteraction = (method) => {
56254
- if (method.protocolV2UiInteraction)
56255
- return method.protocolV2UiInteraction;
56256
- const operation = method.name;
56257
- if (!operation)
56258
- return undefined;
56259
- const isAddress = /getAddress(?:ByLoop)?$/i.test(operation);
56260
- const isPublicKey = /getPublicKey$/i.test(operation);
56261
- if (isAddress || isPublicKey) {
56262
- const paramsDisplay = getDisplayState(method.params);
56263
- const payloadDisplay = getDisplayState(method.payload);
56264
- const display = paramsDisplay.seen ? paramsDisplay : payloadDisplay;
56265
- const shouldDisplay = display.seen ? display.enabled : isAddress;
56266
- if (!shouldDisplay)
56267
- return undefined;
56268
- return createMethodInteraction(isAddress ? 'address-confirmation' : 'public-key-confirmation', operation);
56269
- }
56270
- if (/sign/i.test(operation) || /verifyMessage$/i.test(operation) || operation === 'lnurlAuth') {
56271
- return createMethodInteraction('signing-confirmation', operation);
56272
- }
56273
- if (operation === 'nostrEncryptMessage' || operation === 'nostrDecryptMessage') {
56274
- const display = getDisplayState(method.params);
56275
- if (!display.enabled)
56276
- return undefined;
56277
- return createMethodInteraction('signing-confirmation', operation);
56278
- }
56279
- if (operation === 'cipherKeyValue' && hasCipherConfirmation(method.params)) {
56280
- return createMethodInteraction('signing-confirmation', operation);
56281
- }
56282
- return undefined;
56283
- };
56284
- class ProtocolV2UiInteractionCoordinator {
56285
- constructor(device, postMessage) {
56286
- this.opened = false;
56287
- this.closed = false;
56288
- this.device = device;
56289
- this.postMessage = postMessage;
56290
- }
56291
- enterMethodInteraction(interaction) {
56292
- var _a, _b, _c, _d;
56293
- this.methodInteraction = interaction;
56294
- if (!interaction)
56295
- return;
56296
- const { request } = interaction, metadata = __rest(interaction, ["request"]);
56297
- const interactionMeta = (_b = (_a = this.device).createProtocolV2UiPhaseMetadata) === null || _b === void 0 ? void 0 : _b.call(_a, request === 'pin' ? 'pin' : 'button', 'start');
56298
- this.emit(request === 'pin' ? UI_REQUEST.REQUEST_PIN : UI_REQUEST.REQUEST_BUTTON, Object.assign(Object.assign({ device: this.device.toMessageObject() }, metadata), (interactionMeta ? { interaction: interactionMeta } : {})), `method:${request}:${interaction.reason}:${(_c = interaction.page) !== null && _c !== void 0 ? _c : ''}:${(_d = interaction.operation) !== null && _d !== void 0 ? _d : ''}`);
56299
- }
56300
- enterUnlockInteraction(method) {
56301
- var _a, _b;
56302
- const interaction = (_b = (_a = this.device).createProtocolV2UiPhaseMetadata) === null || _b === void 0 ? void 0 : _b.call(_a, 'pin', 'start');
56303
- this.emit(UI_REQUEST.REQUEST_PIN, Object.assign({ device: this.device.toMessageObject(), source: 'unlock-coordinator', reason: 'device-locked', deviceOnly: true, method }, (interaction ? { interaction } : {})), `unlock:${method !== null && method !== void 0 ? method : ''}`);
56304
- return interaction;
56305
- }
56306
- resumeMethodInteraction() {
56307
- this.enterMethodInteraction(this.methodInteraction);
56308
- }
56309
- close(options = {}) {
56310
- var _a, _b, _c, _d;
56311
- if ((!options.protocolV2Operation && !this.device.isProtocolV2()) ||
56312
- (!options.ensureOperationClose &&
56313
- !this.opened &&
56314
- !((_b = (_a = this.device).hasOpenProtocolV2UiInteraction) === null || _b === void 0 ? void 0 : _b.call(_a))) ||
56315
- this.closed)
56316
- return false;
56317
- this.closed = true;
56318
- const interaction = (_d = (_c = this.device).finishProtocolV2UiInteraction) === null || _d === void 0 ? void 0 : _d.call(_c, 'succeeded', {
56319
- ensureMetadata: options.ensureOperationClose,
56320
- });
56321
- const device = this.device.toMessageObject();
56322
- this.postMessage(interaction
56323
- ? createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW, Object.assign(Object.assign({}, interaction), (device ? { device } : {})))
56324
- : createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW));
56325
- return true;
56326
- }
56327
- emit(type, payload, phase) {
56328
- if (!this.device.isProtocolV2() || this.closed || this.currentPhase === phase)
56329
- return;
56330
- this.currentPhase = phase;
56331
- this.opened = true;
56332
- const message = type === UI_REQUEST.REQUEST_BUTTON
56333
- ? createUiMessage(UI_REQUEST.REQUEST_BUTTON, payload)
56334
- : createUiMessage(UI_REQUEST.REQUEST_PIN, payload);
56335
- this.postMessage(message);
56336
- }
56337
- }
56338
-
56339
- const Log$3 = getLogger(exports.LoggerNames.Core);
56340
- const createProtocolV2UnlockContext = () => ({
56341
- preflightCompleted: false,
56342
- preflightStatusRefreshed: false,
56343
- preflightMainPinSelected: false,
56344
- });
56345
- const resolvePreUnlockPinType = (method) => {
56346
- var _a, _b;
56347
- if (method.protocolV2PreUnlockPinType !== undefined) {
56348
- return method.protocolV2PreUnlockPinType;
56349
- }
56350
- const targetsKnownHiddenWallet = method.useDevicePassphraseState &&
56351
- ((_a = method.payload) === null || _a === void 0 ? void 0 : _a.useEmptyPassphrase) !== true &&
56352
- typeof ((_b = method.payload) === null || _b === void 0 ? void 0 : _b.passphraseState) === 'string' &&
56353
- method.payload.passphraseState.length > 0;
56354
- return targetsKnownHiddenWallet ? hdTransport.DeviceSessionPinType.Any : hdTransport.DeviceSessionPinType.Main;
56355
- };
56356
- function runMethodWithUnlockPolicy(method, device, options = {}) {
56357
- var _a, _b, _c;
56358
- return __awaiter(this, void 0, void 0, function* () {
56359
- const { context = createProtocolV2UnlockContext(), uiCoordinator, afterStatusBeforeUnlock, prepare, run: configuredRun, } = options;
56360
- const run = configuredRun !== null && configuredRun !== void 0 ? configuredRun : (() => method.run());
56361
- method.protocolV2UnlockContext = context;
56362
- const shouldEmitUi = isProtocolV2UiEnabled(method);
56363
- const shouldCoordinateUi = shouldEmitUi && uiCoordinator !== undefined;
56364
- const requiresPreUnlock = device.isProtocolV2() &&
56365
- (method.useDevicePassphraseState || method.unlockPolicy === 'unlock-before-run') &&
56366
- ((_a = device.state) === null || _a === void 0 ? void 0 : _a.status.initialized) !== false &&
56367
- !((_b = device.isBootloader) === null || _b === void 0 ? void 0 : _b.call(device)) &&
56368
- !((_c = device.isRomloader) === null || _c === void 0 ? void 0 : _c.call(device));
56369
- if (requiresPreUnlock && !context.preflightCompleted) {
56370
- const status = yield refreshProtocolV2DeviceStatus(device);
56371
- if (typeof (status === null || status === void 0 ? void 0 : status.unlocked) !== 'boolean') {
56372
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 DeviceStatus did not report an explicit unlock state.');
56373
- }
56374
- yield (afterStatusBeforeUnlock === null || afterStatusBeforeUnlock === void 0 ? void 0 : afterStatusBeforeUnlock());
56375
- if (!status.unlocked) {
56376
- const preUnlockPinType = resolvePreUnlockPinType(method);
56377
- const unlockInteraction = shouldCoordinateUi
56378
- ? uiCoordinator.enterUnlockInteraction(method.name)
56379
- : undefined;
56380
- const unlockedStatus = yield device.unlockDevice(preUnlockPinType, shouldCoordinateUi
56381
- ? { emitUiEvent: false, interaction: unlockInteraction }
56382
- : {
56383
- source: 'unlock-coordinator',
56384
- reason: 'device-locked',
56385
- deviceOnly: true,
56386
- method: method.name,
56387
- });
56388
- if ((unlockedStatus === null || unlockedStatus === void 0 ? void 0 : unlockedStatus.unlocked) !== true) {
56389
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 device remained locked after the unlock flow.');
56390
- }
56391
- context.preflightMainPinSelected = preUnlockPinType === hdTransport.DeviceSessionPinType.Main;
56392
- Log$3.debug('Protocol V2 pre-unlock completed', { method: method.name });
56393
- }
56394
- context.preflightStatusRefreshed = true;
56395
- context.preflightCompleted = true;
56396
- }
56397
- yield (prepare === null || prepare === void 0 ? void 0 : prepare());
56398
- if (shouldCoordinateUi) {
56399
- uiCoordinator.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
56400
- }
56401
- return run();
56402
- });
56403
- }
56404
-
56405
56639
  const Mainnet = 'mainnet';
56406
56640
  const MoneroNetworkTypes = new Map([
56407
56641
  [Mainnet, hdTransport.Messages.MoneroNetworkType.MAINNET],