@onekeyfe/hd-core 1.2.0-alpha.169 → 1.2.0-alpha.170

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.
@@ -375,18 +375,6 @@ const isProtocolV2TargetStatusInProgress = (
375
375
  normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_PENDING ||
376
376
  normalizeProtocolV2TargetStatus(status) === PROTOCOL_V2_TARGET_STATUS_IN_PROGRESS;
377
377
 
378
- const getProtocolV2FirmwareStatusFingerprint = (
379
- statusTargets: ProtocolV2FirmwareUpdateStatusTarget[]
380
- ) =>
381
- JSON.stringify(
382
- statusTargets.map(target => ({
383
- targetId: normalizeProtocolV2TargetId(target.target_id) ?? target.target_id,
384
- status: normalizeProtocolV2TargetStatus(target.status) ?? target.status ?? null,
385
- payloadVersion: target.payload_version ?? null,
386
- path: target.path ?? null,
387
- }))
388
- );
389
-
390
378
  const isProtocolV2TargetStatusFailed = (status: ProtocolV2FirmwareUpdateStatusTarget['status']) => {
391
379
  const normalizedStatus = normalizeProtocolV2TargetStatus(status);
392
380
  return (
@@ -542,10 +530,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
542
530
 
543
531
  private protocolV2InstallBaselineVersions = new Map<number, string>();
544
532
 
545
- private protocolV2InstallStatusBaseline?: ProtocolV2FirmwareUpdateStatusTarget[];
546
-
547
533
  private protocolV2InstallNeedsBleReconnect = false;
548
534
 
535
+ private protocolV2InstallRequestConfirmed = false;
536
+
549
537
  private protocolV2LastRuntimeProbeFeatures?: Features;
550
538
 
551
539
  private protocolV2LastTransferProgress?: number;
@@ -2228,19 +2216,41 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2228
2216
  }
2229
2217
  }
2230
2218
 
2219
+ private collectProtocolV2LiveTargetIds(
2220
+ statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
2221
+ expectedTargetIds: Set<number>,
2222
+ liveTargetIds: Set<number>
2223
+ ) {
2224
+ statusTargets.forEach(target => {
2225
+ const targetId = normalizeProtocolV2TargetId(target.target_id);
2226
+ if (
2227
+ targetId !== undefined &&
2228
+ expectedTargetIds.has(targetId) &&
2229
+ isProtocolV2TargetStatusInProgress(target.status)
2230
+ ) {
2231
+ liveTargetIds.add(targetId);
2232
+ }
2233
+ });
2234
+ }
2235
+
2231
2236
  private assertProtocolV2TargetStatus(
2232
2237
  statusTargets: ProtocolV2FirmwareUpdateStatusTarget[],
2233
2238
  expectedTargetIds: Set<number>,
2234
- expectedPaths = new Map<number, string>()
2239
+ expectedPaths?: Map<number, string>,
2240
+ liveTargetIds?: Set<number>
2235
2241
  ) {
2242
+ const resolvedExpectedPaths = expectedPaths ?? new Map<number, string>();
2236
2243
  Log.log(
2237
2244
  `[FirmwareUpdateV4] DeviceFirmwareUpdateStatus records=${JSON.stringify(statusTargets)}`
2238
2245
  );
2239
- const failedTarget = statusTargets.find(
2240
- target =>
2241
- expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1) &&
2242
- isProtocolV2TargetStatusFailed(target.status)
2243
- );
2246
+ const failedTarget = statusTargets.find(target => {
2247
+ const targetId = normalizeProtocolV2TargetId(target.target_id) ?? -1;
2248
+ return (
2249
+ expectedTargetIds.has(targetId) &&
2250
+ isProtocolV2TargetStatusFailed(target.status) &&
2251
+ (!liveTargetIds || liveTargetIds.has(targetId))
2252
+ );
2253
+ });
2244
2254
  if (failedTarget) {
2245
2255
  const failedTargetDetails = JSON.stringify({
2246
2256
  targetId: failedTarget.target_id,
@@ -2257,6 +2267,27 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2257
2267
  }
2258
2268
  );
2259
2269
  }
2270
+ if (liveTargetIds) {
2271
+ const staleFailedTargets = statusTargets.filter(target => {
2272
+ const targetId = normalizeProtocolV2TargetId(target.target_id) ?? -1;
2273
+ return (
2274
+ expectedTargetIds.has(targetId) &&
2275
+ isProtocolV2TargetStatusFailed(target.status) &&
2276
+ !liveTargetIds.has(targetId)
2277
+ );
2278
+ });
2279
+ if (staleFailedTargets.length > 0) {
2280
+ Log.log(
2281
+ `[FirmwareUpdateV4] ignoring stale failed firmware status until those targets start: ${JSON.stringify(
2282
+ staleFailedTargets.map(target => ({
2283
+ targetId: target.target_id,
2284
+ status: target.status,
2285
+ path: target.path,
2286
+ }))
2287
+ )}`
2288
+ );
2289
+ }
2290
+ }
2260
2291
 
2261
2292
  const matchingTargets = statusTargets.filter(target =>
2262
2293
  expectedTargetIds.has(normalizeProtocolV2TargetId(target.target_id) ?? -1)
@@ -2264,10 +2295,15 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2264
2295
  const seenTargetIds = new Set<number>();
2265
2296
  for (const target of matchingTargets) {
2266
2297
  const targetId = normalizeProtocolV2TargetId(target.target_id);
2298
+ const pathConflictsWithCurrentInstall =
2299
+ Boolean(target.path) &&
2300
+ Boolean(resolvedExpectedPaths.get(targetId)) &&
2301
+ target.path !== resolvedExpectedPaths.get(targetId) &&
2302
+ (!liveTargetIds || liveTargetIds.has(targetId));
2267
2303
  if (
2268
2304
  targetId === undefined ||
2269
2305
  seenTargetIds.has(targetId) ||
2270
- (target.path && expectedPaths.get(targetId) && target.path !== expectedPaths.get(targetId))
2306
+ pathConflictsWithCurrentInstall
2271
2307
  ) {
2272
2308
  Log.error(`[FirmwareUpdateV4] install status conflicts with target=${target.target_id}`);
2273
2309
  throw ERRORS.TypedError(
@@ -2399,12 +2435,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2399
2435
  ) {
2400
2436
  const expectedTargetIds = new Set(targets.map(target => target.target_id));
2401
2437
  const expectedPaths = new Map(targets.map(target => [target.target_id, target.path]));
2402
- const confirmationStartedAt = Date.now();
2403
- let installStartedAt = requireCurrentInstallStatus ? undefined : confirmationStartedAt;
2404
- const effectiveBaselineStatusTargets = this.protocolV2InstallStatusBaseline;
2405
- const baselineStatusFingerprint = effectiveBaselineStatusTargets
2406
- ? getProtocolV2FirmwareStatusFingerprint(effectiveBaselineStatusTargets)
2407
- : undefined;
2438
+ const startTime = Date.now();
2408
2439
  let lastError: unknown;
2409
2440
  // USB may keep the loader link usable. BLE can release it as installation starts, so its
2410
2441
  // recovery reconnects directly to status polling without generic Ping or DeviceInfo probes.
@@ -2412,17 +2443,18 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2412
2443
  this.protocolV2InstallNeedsBleReconnect = false;
2413
2444
  let deviceInfo: ProtocolV2DeviceInfo | undefined;
2414
2445
  let bleInstallLinkReady = false;
2415
- let installEvidenceObserved = false;
2446
+ let installEvidenceObserved = this.protocolV2InstallRequestConfirmed;
2416
2447
  let currentInstallStatusObserved = false;
2448
+ const liveTargetIds = new Set<number>();
2417
2449
 
2418
- while (Date.now() - (installStartedAt ?? confirmationStartedAt) < PROTOCOL_V2_INSTALL_TIMEOUT) {
2450
+ while (Date.now() - startTime < PROTOCOL_V2_INSTALL_TIMEOUT) {
2419
2451
  // A transport release caused by an explicit workflow cancellation must not
2420
2452
  // be mistaken for the expected device reboot during installation.
2421
2453
  this.throwIfAborted();
2422
2454
  try {
2423
2455
  if (shouldReconnect) {
2424
2456
  const isBleInstallReconnect = this.isBleReconnect();
2425
- await this.reconnectProtocolV2Device({ skipBleProtocolProbe: isBleInstallReconnect });
2457
+ await this.reconnectProtocolV2Device({ skipProtocolProbe: true });
2426
2458
  bleInstallLinkReady = isBleInstallReconnect;
2427
2459
  deviceInfo = isBleInstallReconnect
2428
2460
  ? undefined
@@ -2453,33 +2485,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2453
2485
  statusResponse.type === 'DeviceFirmwareUpdateStatus'
2454
2486
  ? ((statusResponse.message.records ?? []) as ProtocolV2FirmwareUpdateStatusTarget[])
2455
2487
  : [];
2456
- const hasPendingOrInProgressTarget = statusTargets.some(target => {
2457
- const targetId = normalizeProtocolV2TargetId(target.target_id);
2458
- return (
2459
- targetId !== undefined &&
2460
- expectedTargetIds.has(targetId) &&
2461
- isProtocolV2TargetStatusInProgress(target.status)
2462
- );
2463
- });
2464
- const isExpectedStatusSnapshot =
2465
- statusTargets.length === targets.length &&
2466
- targets.every((expectedTarget, index) => {
2467
- const statusTarget = statusTargets[index];
2468
- return (
2469
- statusTarget !== undefined &&
2470
- normalizeProtocolV2TargetId(statusTarget.target_id) === expectedTarget.target_id &&
2471
- statusTarget.path === expectedTarget.path
2472
- );
2473
- });
2474
- const statusFingerprint = getProtocolV2FirmwareStatusFingerprint(statusTargets);
2475
- const hasCurrentInstallTransition =
2476
- baselineStatusFingerprint !== undefined
2477
- ? isExpectedStatusSnapshot && statusFingerprint !== baselineStatusFingerprint
2478
- : hasPendingOrInProgressTarget;
2479
- if (!currentInstallStatusObserved && hasCurrentInstallTransition) {
2488
+ this.collectProtocolV2LiveTargetIds(statusTargets, expectedTargetIds, liveTargetIds);
2489
+ if (liveTargetIds.size > 0) {
2480
2490
  currentInstallStatusObserved = true;
2481
- installStartedAt = Date.now();
2482
- Log.log('[FirmwareUpdateV4] current firmware install records observed');
2483
2491
  }
2484
2492
  const hasMatchingTargetStatus = statusTargets.some(target => {
2485
2493
  const targetId = normalizeProtocolV2TargetId(target.target_id);
@@ -2499,7 +2507,12 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2499
2507
  !requireCurrentInstallStatus || currentInstallStatusObserved;
2500
2508
  if (
2501
2509
  shouldVerifyTargetCompletion &&
2502
- this.assertProtocolV2TargetStatus(statusTargets, expectedTargetIds, expectedPaths)
2510
+ this.assertProtocolV2TargetStatus(
2511
+ statusTargets,
2512
+ expectedTargetIds,
2513
+ expectedPaths,
2514
+ requireCurrentInstallStatus ? liveTargetIds : undefined
2515
+ )
2503
2516
  ) {
2504
2517
  return;
2505
2518
  }
@@ -2760,19 +2773,53 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2760
2773
  );
2761
2774
  }
2762
2775
 
2763
- private async reconnectProtocolV2Device(options?: { skipBleProtocolProbe?: boolean }) {
2776
+ private async reconnectProtocolV2Device(options?: { skipProtocolProbe?: boolean }) {
2764
2777
  if (this.isBleReconnect()) {
2765
- await this.acquireProtocolV2BleDevice(options?.skipBleProtocolProbe);
2778
+ await this.acquireProtocolV2BleDevice(options?.skipProtocolProbe);
2766
2779
  return;
2767
2780
  }
2768
2781
 
2769
2782
  const deviceDiff = await this.device.deviceConnector?.enumerate();
2770
2783
  const devicesDescriptor = deviceDiff?.descriptors ?? [];
2784
+ const env = DataManager.getSettings('env');
2785
+ const isWebUsb = DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env);
2786
+
2787
+ if (isWebUsb && options?.skipProtocolProbe) {
2788
+ const expectedSerialNumber = this.protocolV2ExpectedSerialNumber?.trim();
2789
+ const expectedPath = this.protocolV2ExpectedPath?.trim();
2790
+ const reconnectDescriptor = devicesDescriptor.find(descriptor => {
2791
+ const descriptorPath = descriptor.path?.trim();
2792
+ return (
2793
+ (!!expectedSerialNumber && descriptorPath === expectedSerialNumber) ||
2794
+ (!!expectedPath && descriptorPath === expectedPath)
2795
+ );
2796
+ });
2797
+ if (!reconnectDescriptor?.path) {
2798
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
2799
+ }
2800
+ this.device.updateDescriptor(
2801
+ {
2802
+ ...reconnectDescriptor,
2803
+ protocolType: PROTOCOL_V2_CONNECT_PROTOCOL,
2804
+ },
2805
+ true
2806
+ );
2807
+ const mainId = await this.device.deviceConnector?.acquire(
2808
+ reconnectDescriptor.path,
2809
+ null,
2810
+ undefined,
2811
+ PROTOCOL_V2_CONNECT_PROTOCOL,
2812
+ undefined,
2813
+ undefined,
2814
+ true
2815
+ );
2816
+ this.device.mainId = typeof mainId === 'string' ? mainId : reconnectDescriptor.path;
2817
+ this.device.commands.disposed = false;
2818
+ this.device.getCommands().mainId = this.device.mainId;
2819
+ return;
2820
+ }
2771
2821
 
2772
- if (
2773
- DataManager.isBrowserWebUsb(DataManager.getSettings('env')) &&
2774
- devicesDescriptor.length === 1
2775
- ) {
2822
+ if (isWebUsb && devicesDescriptor.length === 1) {
2776
2823
  const descriptor = devicesDescriptor[0];
2777
2824
  if (!this.protocolV2ExpectedSerialNumber && descriptor.path !== this.protocolV2ExpectedPath) {
2778
2825
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
@@ -2923,31 +2970,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2923
2970
  targets: Array<{ target_id: number; path: string }>;
2924
2971
  }) {
2925
2972
  this.protocolV2LastRuntimeProbeFeatures = undefined;
2926
- this.protocolV2InstallStatusBaseline = undefined;
2927
2973
  this.protocolV2InstallNeedsBleReconnect = false;
2974
+ this.protocolV2InstallRequestConfirmed = false;
2928
2975
  const commands = this.device.getCommands();
2929
2976
  await commands.typedCall('DeviceFirmwareUpdateStage', 'Success', { targets });
2930
- try {
2931
- const baselineStatusResponse = await commands.typedCall(
2932
- 'DeviceFirmwareUpdateStatusGet',
2933
- ['DeviceFirmwareUpdateStatus', 'Success'],
2934
- {
2935
- fields: {
2936
- status: true,
2937
- payload_version: true,
2938
- path: true,
2939
- },
2940
- },
2941
- { timeoutMs: PROTOCOL_V2_FIRMWARE_STATUS_RESPONSE_TIMEOUT }
2942
- );
2943
- this.protocolV2InstallStatusBaseline =
2944
- baselineStatusResponse.type === 'DeviceFirmwareUpdateStatus'
2945
- ? ((baselineStatusResponse.message.records ??
2946
- []) as ProtocolV2FirmwareUpdateStatusTarget[])
2947
- : [];
2948
- } catch (error) {
2949
- Log.log('[FirmwareUpdateV4] unable to capture pre-install firmware status: ', error);
2950
- }
2951
2977
  this.device.setCancelableAction(() => commands.cancelDevice());
2952
2978
  const interaction = this.device.createProtocolV2UiPhaseMetadata('button', 'start');
2953
2979
  this.postMessage(
@@ -2962,7 +2988,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
2962
2988
  })
2963
2989
  );
2964
2990
  try {
2965
- await commands.call('DeviceFirmwareUpdateRequest', {});
2991
+ await commands.typedCall('DeviceFirmwareUpdateRequest', 'Success', {});
2992
+ this.protocolV2InstallRequestConfirmed = true;
2993
+ this.postProgressMessage(1, 'installingFirmware');
2994
+ Log.log('[FirmwareUpdateV4] firmware install confirmed by device response');
2966
2995
  } catch (error) {
2967
2996
  this.throwIfAborted();
2968
2997
  if (!isProtocolV2DeviceDisconnectedError(error)) {
package/src/core/index.ts CHANGED
@@ -1220,6 +1220,9 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1220
1220
  ','
1221
1221
  )}`
1222
1222
  );
1223
+ // Abort before rejecting: rejectRequest releases the task and would make
1224
+ // its AbortController unreachable to an in-flight method loop.
1225
+ requestQueue.abortRequestsByConnectId(connectId);
1223
1226
  const canceledDevices: Device[] = [];
1224
1227
  const interruptDevice = (device: Device | undefined, deviceConnectId: string) => {
1225
1228
  if (!device || canceledDevices.includes(device)) {
@@ -1244,7 +1247,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1244
1247
  }
1245
1248
  }
1246
1249
  interruptDevice(deviceCacheMap.get(connectId), connectId);
1247
- requestQueue.abortRequestsByConnectId(connectId);
1248
1250
  pollingManager.stop(connectId);
1249
1251
  } catch (e) {
1250
1252
  Log.error('Cancel API Error: ', e);
@@ -1253,6 +1255,8 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1253
1255
  const env = DataManager.getSettings('env');
1254
1256
  if (DataManager.isBleConnect(env)) {
1255
1257
  Log.debug('Cancel Api all _deviceList: ');
1258
+ // Keep method abort signals observable until every active task is rejected.
1259
+ requestQueue.abortAllRequests();
1256
1260
  const canceledDevices: Device[] = [];
1257
1261
  const interruptDevice = (device?: Device) => {
1258
1262
  if (!device || canceledDevices.includes(device)) {
@@ -1277,7 +1281,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1277
1281
  }
1278
1282
  }
1279
1283
  deviceCacheMap.forEach(interruptDevice);
1280
- requestQueue.abortAllRequests();
1281
1284
  pollingManager.stopAll();
1282
1285
  } else {
1283
1286
  _deviceList?.allDevices().forEach(device => {
@@ -105,9 +105,7 @@ export default class DeviceConnector {
105
105
  const transport = this.getActiveTransport();
106
106
  let res;
107
107
  if (DataManager.isBleConnect(env)) {
108
- const acquireInput: Parameters<Transport['acquire']>[0] & {
109
- skipProtocolProbe?: boolean;
110
- } = {
108
+ const acquireInput: Parameters<Transport['acquire']>[0] = {
111
109
  uuid: path,
112
110
  forceCleanRunPromise,
113
111
  expectedProtocol,
@@ -123,6 +121,7 @@ export default class DeviceConnector {
123
121
  expectedProtocol,
124
122
  protocolHint,
125
123
  forceProtocolDetection,
124
+ skipProtocolProbe,
126
125
  });
127
126
  }
128
127
  if (expectedProtocol) {
@@ -59,7 +59,15 @@ export function isProtocolV2ResourceArchiveEntryName(entryName: string): boolean
59
59
  return (
60
60
  fileName.length > 0 &&
61
61
  !fileName.startsWith('.') &&
62
- !parts.some(part => part === '__MACOSX' || part === '.' || part === '..' || part === '')
62
+ !parts.some(
63
+ part =>
64
+ !part ||
65
+ part === '.' ||
66
+ part === '..' ||
67
+ part === '.DS_Store' ||
68
+ part.startsWith('._') ||
69
+ part.toUpperCase() === '__MACOSX'
70
+ )
63
71
  );
64
72
  }
65
73