@onekeyfe/hwk-ledger-adapter 1.1.26-alpha.20 → 1.1.26-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -114,9 +114,9 @@ declare class LedgerAdapter implements IHardwareWallet {
114
114
  dispose(): Promise<void>;
115
115
  uiResponse(response: UiResponseEvent): void;
116
116
  searchDevices(options?: SearchDevicesOptions): Promise<DeviceInfo[]>;
117
- /** Compact snapshot helpers for diagnostic logs. */
118
117
  private _snapshotSessions;
119
118
  private _snapshotDiscovered;
119
+ private _evictAllSessions;
120
120
  private static readonly MAX_BUSINESS_RETRY_BUDGET;
121
121
  private static readonly STUCK_APP_RETRY_DELAY_MS;
122
122
  private static readonly MAX_DOCONNECT_CONFIRMS;
package/dist/index.d.ts CHANGED
@@ -114,9 +114,9 @@ declare class LedgerAdapter implements IHardwareWallet {
114
114
  dispose(): Promise<void>;
115
115
  uiResponse(response: UiResponseEvent): void;
116
116
  searchDevices(options?: SearchDevicesOptions): Promise<DeviceInfo[]>;
117
- /** Compact snapshot helpers for diagnostic logs. */
118
117
  private _snapshotSessions;
119
118
  private _snapshotDiscovered;
119
+ private _evictAllSessions;
120
120
  private static readonly MAX_BUSINESS_RETRY_BUDGET;
121
121
  private static readonly STUCK_APP_RETRY_DELAY_MS;
122
122
  private static readonly MAX_DOCONNECT_CONFIRMS;
package/dist/index.js CHANGED
@@ -347,6 +347,15 @@ function isAppNotInstalledError(err) {
347
347
  if (hasStatusCode(err, APP_NOT_INSTALLED_CODES)) return true;
348
348
  return false;
349
349
  }
350
+ function isOutOfMemoryError(err) {
351
+ if (!err || typeof err !== "object") return false;
352
+ const e = err;
353
+ if (e._tag === "OutOfMemoryDAError") return true;
354
+ if (typeof e.message === "string" && /out of memory|not enough.*space|insufficient.*memory/i.test(e.message)) {
355
+ return true;
356
+ }
357
+ return false;
358
+ }
350
359
  function isDeviceDisconnectedError(err) {
351
360
  if (!err || typeof err !== "object") return false;
352
361
  const e = err;
@@ -409,6 +418,8 @@ function mapLedgerError(err, opts) {
409
418
  code = import_hwk_adapter_core.HardwareErrorCode.WrongApp;
410
419
  } else if (isAppNotInstalledError(err)) {
411
420
  code = import_hwk_adapter_core.HardwareErrorCode.AppNotInstalled;
421
+ } else if (isOutOfMemoryError(err)) {
422
+ code = import_hwk_adapter_core.HardwareErrorCode.DeviceOutOfMemory;
412
423
  } else if (isDeviceDisconnectedError(err)) {
413
424
  code = import_hwk_adapter_core.HardwareErrorCode.DeviceDisconnected;
414
425
  } else if (isTimeoutError(err)) {
@@ -666,7 +677,6 @@ var _LedgerAdapter = class _LedgerAdapter {
666
677
  );
667
678
  return Array.from(this._discoveredDevices.values());
668
679
  }
669
- /** Compact snapshot helpers for diagnostic logs. */
670
680
  _snapshotSessions() {
671
681
  return [...this._sessions.entries()].map(([connectId, sessionId]) => ({
672
682
  connectId,
@@ -681,6 +691,30 @@ var _LedgerAdapter = class _LedgerAdapter {
681
691
  serial: info.serialNumber
682
692
  }));
683
693
  }
694
+ // Enforce USB single-session invariant — see connectDevice.
695
+ async _evictAllSessions(reason) {
696
+ if (this._sessions.size === 0) return;
697
+ const stale = [...this._sessions.entries()];
698
+ debugLog(
699
+ "[SESS-DBG] evictAllSessions reason=",
700
+ reason,
701
+ "evicting=",
702
+ stale.map(([cid, sid]) => ({ connectId: cid, sessionId: sid }))
703
+ );
704
+ this._sessions.clear();
705
+ for (const [, sid] of stale) {
706
+ try {
707
+ await this.connector.disconnect(sid);
708
+ } catch (err) {
709
+ debugLog(
710
+ "[SESS-DBG] evictAllSessions disconnect failed sessionId=",
711
+ sid,
712
+ "err=",
713
+ err?.message
714
+ );
715
+ }
716
+ }
717
+ }
684
718
  static _createDeviceBusyError(method) {
685
719
  return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
686
720
  code: import_hwk_adapter_core2.HardwareErrorCode.DeviceBusy
@@ -701,6 +735,9 @@ var _LedgerAdapter = class _LedgerAdapter {
701
735
  code: import_hwk_adapter_core2.HardwareErrorCode.DeviceNotFound
702
736
  });
703
737
  }
738
+ if (!isLedgerBleConnectionType(this.connector.connectionType)) {
739
+ await this._evictAllSessions("connectDevice");
740
+ }
704
741
  await this._ensureDevicePermission(connectId);
705
742
  const session = await this.connector.connect(connectId);
706
743
  const prev = this._sessions.get(connectId);
@@ -1122,13 +1159,24 @@ var _LedgerAdapter = class _LedgerAdapter {
1122
1159
  return connectId;
1123
1160
  }
1124
1161
  if (!connectId && this._sessions.size > 0) {
1162
+ if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
1163
+ debugLog(
1164
+ "[SESS-DBG] ensureConnected ABORT multiple USB sessions present sessions=",
1165
+ this._snapshotSessions()
1166
+ );
1167
+ throw Object.assign(
1168
+ new Error(
1169
+ "Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
1170
+ ),
1171
+ { code: import_hwk_adapter_core2.HardwareErrorCode.DeviceOneDeviceOnly }
1172
+ );
1173
+ }
1125
1174
  const ambient = this._sessions.keys().next().value;
1126
1175
  debugLog(
1127
1176
  "[SESS-DBG] ensureConnected RESOLVED path=ambient-fallback chosenConnectId=",
1128
1177
  ambient,
1129
1178
  "sessionId=",
1130
- this._sessions.get(ambient),
1131
- "WARNING: caller passed empty connectId, routing to first session entry"
1179
+ this._sessions.get(ambient)
1132
1180
  );
1133
1181
  return ambient;
1134
1182
  }
@@ -1692,7 +1740,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1692
1740
  const tag = err && typeof err === "object" ? err._tag : void 0;
1693
1741
  if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
1694
1742
  const e = err;
1695
- const params = e.code === import_hwk_adapter_core2.HardwareErrorCode.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : void 0;
1743
+ const params = e.code === import_hwk_adapter_core2.HardwareErrorCode.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
1696
1744
  return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
1697
1745
  }
1698
1746
  const mapped = mapLedgerError(err);
@@ -1742,7 +1790,7 @@ _LedgerAdapter.MAX_DOCONNECT_CONFIRMS = 3;
1742
1790
  var LedgerAdapter = _LedgerAdapter;
1743
1791
 
1744
1792
  // src/connector/LedgerConnectorBase.ts
1745
- var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
1793
+ var import_hwk_adapter_core13 = require("@onekeyfe/hwk-adapter-core");
1746
1794
 
1747
1795
  // src/device/LedgerDeviceManager.ts
1748
1796
  var LedgerDeviceManager = class {
@@ -3158,6 +3206,7 @@ async function _createTrx(ctx, sessionId) {
3158
3206
 
3159
3207
  // src/device-apps/DeviceApps.ts
3160
3208
  var import_device_management_kit3 = require("@ledgerhq/device-management-kit");
3209
+ var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
3161
3210
  var import_rxjs = require("rxjs");
3162
3211
  var INSTALL_TIMEOUT_MS = 5 * 6e4;
3163
3212
  var DeviceApps = class {
@@ -3223,6 +3272,72 @@ var DeviceApps = class {
3223
3272
  hwVersion: v.hwVersion
3224
3273
  };
3225
3274
  }
3275
+ /**
3276
+ * Pre-flight space check. Mirrors DMK's PredictOutOfMemoryTask using the
3277
+ * data we already expose (listInstalled / listAvailable / getOsVersion).
3278
+ * Fails fast with HardwareErrorCode.DeviceOutOfMemory before issuing the
3279
+ * actual install — saves the user the time-to-fail of DMK's state machine.
3280
+ */
3281
+ async _assertEnoughSpace(appName) {
3282
+ let osVersion;
3283
+ let installed;
3284
+ let available;
3285
+ try {
3286
+ [osVersion, installed, available] = await Promise.all([
3287
+ this._fetchOsVersion(),
3288
+ this.listInstalled(),
3289
+ this.listAvailable()
3290
+ ]);
3291
+ } catch (err) {
3292
+ debugLog("[DeviceApps] precheck skipped:", err?.message);
3293
+ return;
3294
+ }
3295
+ if (installed.some((a) => a.versionName === appName)) return;
3296
+ const target = available.find((a) => a.versionName === appName);
3297
+ if (!target) return;
3298
+ const capacity = getDeviceCapacity(osVersion.targetId);
3299
+ if (!capacity) return;
3300
+ const blockSize = capacity.getBlockSize(osVersion.seVersion);
3301
+ const totalBlocks = Math.floor(capacity.memorySize / blockSize);
3302
+ const blocks = (b) => Math.ceil((b ?? 0) / blockSize);
3303
+ const usedBlocks = installed.reduce((sum, a) => sum + blocks(a.bytes), 0);
3304
+ const neededBlocks = blocks(target.bytes);
3305
+ debugLog(
3306
+ "[DeviceApps] space check",
3307
+ "targetId=",
3308
+ osVersion.targetId.toString(16),
3309
+ "memorySize=",
3310
+ capacity.memorySize,
3311
+ "blockSize=",
3312
+ blockSize,
3313
+ "usedBlocks=",
3314
+ usedBlocks,
3315
+ "neededBlocks=",
3316
+ neededBlocks,
3317
+ "totalBlocks=",
3318
+ totalBlocks
3319
+ );
3320
+ if (usedBlocks + neededBlocks > totalBlocks) {
3321
+ const usedBytes = usedBlocks * blockSize;
3322
+ const neededBytes = neededBlocks * blockSize;
3323
+ const freeBytes = Math.max(0, capacity.memorySize - usedBytes);
3324
+ throw Object.assign(
3325
+ new Error(
3326
+ `Not enough space to install "${appName}": needs ${neededBytes} bytes, ${freeBytes} bytes free of ${capacity.memorySize}.`
3327
+ ),
3328
+ {
3329
+ code: import_hwk_adapter_core12.HardwareErrorCode.DeviceOutOfMemory,
3330
+ _tag: "OutOfMemoryDAError",
3331
+ appName,
3332
+ params: {
3333
+ requiredBytes: neededBytes,
3334
+ availableBytes: freeBytes,
3335
+ totalBytes: capacity.memorySize
3336
+ }
3337
+ }
3338
+ );
3339
+ }
3340
+ }
3226
3341
  // Sole sendCommand wrapper — surfaces unlock-device interaction through
3227
3342
  // the same onInteraction pipeline as the device-action methods.
3228
3343
  async _fetchOsVersion() {
@@ -3244,6 +3359,7 @@ var DeviceApps = class {
3244
3359
  async install(appName, onProgress, options) {
3245
3360
  if (!appName) throw new Error("DeviceApps.install: appName is required");
3246
3361
  debugLog("[DeviceApps] install:", appName);
3362
+ await this._assertEnoughSpace(appName);
3247
3363
  const action = this._dmk.executeDeviceAction({
3248
3364
  sessionId: this._sessionId,
3249
3365
  deviceAction: new this._ledgerKit.InstallAppDeviceAction({
@@ -3284,6 +3400,34 @@ function applicationToMetadata(app) {
3284
3400
  isDevTools: app.isDevTools
3285
3401
  };
3286
3402
  }
3403
+ var DEVICE_CAPACITIES = [
3404
+ // Nano S — block size 4K on fw<2.0, 2K on >=2.0. Default 4K (conservative).
3405
+ {
3406
+ mask: 823132160,
3407
+ capacity: {
3408
+ memorySize: 320 * 1024,
3409
+ getBlockSize: (fw) => parseMajor(fw) >= 2 ? 2 * 1024 : 4 * 1024
3410
+ }
3411
+ },
3412
+ // Nano S Plus
3413
+ { mask: 856686592, capacity: { memorySize: 1533 * 1024, getBlockSize: () => 32 } },
3414
+ // Nano X
3415
+ { mask: 855638016, capacity: { memorySize: 2 * 1024 * 1024, getBlockSize: () => 4 * 1024 } },
3416
+ // Stax
3417
+ { mask: 857735168, capacity: { memorySize: 1533 * 1024, getBlockSize: () => 32 } },
3418
+ // Flex
3419
+ { mask: 858783744, capacity: { memorySize: 1533 * 1024, getBlockSize: () => 32 } },
3420
+ // Apex / Nano Gen5
3421
+ { mask: 859832320, capacity: { memorySize: 1533 * 1024, getBlockSize: () => 32 } }
3422
+ ];
3423
+ function getDeviceCapacity(targetId) {
3424
+ const masked = targetId & 4294901760;
3425
+ return DEVICE_CAPACITIES.find((d) => d.mask === masked)?.capacity;
3426
+ }
3427
+ function parseMajor(version) {
3428
+ const m = /^(\d+)/.exec(version);
3429
+ return m ? Number(m[1]) : 0;
3430
+ }
3287
3431
  var GetOsVersionDeviceAction = class {
3288
3432
  constructor(_deps) {
3289
3433
  this._deps = _deps;
@@ -3398,7 +3542,7 @@ var METHOD_PREFIX_TO_APP_NAME = {
3398
3542
  tron: "Tron"
3399
3543
  };
3400
3544
  var HARDWARE_ERROR_CODE_VALUES = new Set(
3401
- Object.values(import_hwk_adapter_core12.HardwareErrorCode).filter((value) => typeof value === "number")
3545
+ Object.values(import_hwk_adapter_core13.HardwareErrorCode).filter((value) => typeof value === "number")
3402
3546
  );
3403
3547
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
3404
3548
  async function defaultLedgerKitImporter(pkg) {
@@ -3577,7 +3721,7 @@ var LedgerConnectorBase = class {
3577
3721
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
3578
3722
  );
3579
3723
  err._tag = ERROR_TAG.BlePairingTimeout;
3580
- err.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
3724
+ err.code = import_hwk_adapter_core13.HardwareErrorCode.BlePairingTimeout;
3581
3725
  reject(err);
3582
3726
  }, HANG_CEILING_MS);
3583
3727
  });
@@ -3610,7 +3754,7 @@ var LedgerConnectorBase = class {
3610
3754
  "Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
3611
3755
  );
3612
3756
  err._tag = ERROR_TAG.DeviceNotAdvertising;
3613
- err.code = import_hwk_adapter_core12.HardwareErrorCode.DeviceNotFound;
3757
+ err.code = import_hwk_adapter_core13.HardwareErrorCode.DeviceNotFound;
3614
3758
  throw err;
3615
3759
  };
3616
3760
  const doConnect = async (path) => {
@@ -3689,7 +3833,7 @@ var LedgerConnectorBase = class {
3689
3833
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
3690
3834
  );
3691
3835
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
3692
- wrapped.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
3836
+ wrapped.code = import_hwk_adapter_core13.HardwareErrorCode.BlePairingTimeout;
3693
3837
  wrapped.originalError = err;
3694
3838
  throw wrapped;
3695
3839
  }
@@ -3810,7 +3954,7 @@ var LedgerConnectorBase = class {
3810
3954
  this._unwatchSessionState(sessionId);
3811
3955
  this._signerManager?.invalidate(sessionId);
3812
3956
  this._cancellers.get(sessionId)?.({
3813
- code: import_hwk_adapter_core12.HardwareErrorCode.DeviceDisconnected,
3957
+ code: import_hwk_adapter_core13.HardwareErrorCode.DeviceDisconnected,
3814
3958
  tag: "DeviceDisconnected",
3815
3959
  message: "Device disconnected"
3816
3960
  });
@@ -3827,14 +3971,14 @@ var LedgerConnectorBase = class {
3827
3971
  } catch (err) {
3828
3972
  if (isAppStuckByApdu(err)) {
3829
3973
  throw Object.assign(new Error("Ledger app is unresponsive"), {
3830
- code: import_hwk_adapter_core12.HardwareErrorCode.DeviceAppStuck,
3974
+ code: import_hwk_adapter_core13.HardwareErrorCode.DeviceAppStuck,
3831
3975
  _tag: ERROR_TAG.DeviceAppStuck,
3832
3976
  originalError: err
3833
3977
  });
3834
3978
  }
3835
3979
  if (isTransportStuck(err)) {
3836
3980
  throw Object.assign(new Error("Device communication interrupted, please retry"), {
3837
- code: import_hwk_adapter_core12.HardwareErrorCode.TransportError,
3981
+ code: import_hwk_adapter_core13.HardwareErrorCode.TransportError,
3838
3982
  _tag: ERROR_TAG.DeviceTransportStuck,
3839
3983
  originalError: err
3840
3984
  });