@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.mjs CHANGED
@@ -306,6 +306,15 @@ function isAppNotInstalledError(err) {
306
306
  if (hasStatusCode(err, APP_NOT_INSTALLED_CODES)) return true;
307
307
  return false;
308
308
  }
309
+ function isOutOfMemoryError(err) {
310
+ if (!err || typeof err !== "object") return false;
311
+ const e = err;
312
+ if (e._tag === "OutOfMemoryDAError") return true;
313
+ if (typeof e.message === "string" && /out of memory|not enough.*space|insufficient.*memory/i.test(e.message)) {
314
+ return true;
315
+ }
316
+ return false;
317
+ }
309
318
  function isDeviceDisconnectedError(err) {
310
319
  if (!err || typeof err !== "object") return false;
311
320
  const e = err;
@@ -368,6 +377,8 @@ function mapLedgerError(err, opts) {
368
377
  code = HardwareErrorCode.WrongApp;
369
378
  } else if (isAppNotInstalledError(err)) {
370
379
  code = HardwareErrorCode.AppNotInstalled;
380
+ } else if (isOutOfMemoryError(err)) {
381
+ code = HardwareErrorCode.DeviceOutOfMemory;
371
382
  } else if (isDeviceDisconnectedError(err)) {
372
383
  code = HardwareErrorCode.DeviceDisconnected;
373
384
  } else if (isTimeoutError(err)) {
@@ -625,7 +636,6 @@ var _LedgerAdapter = class _LedgerAdapter {
625
636
  );
626
637
  return Array.from(this._discoveredDevices.values());
627
638
  }
628
- /** Compact snapshot helpers for diagnostic logs. */
629
639
  _snapshotSessions() {
630
640
  return [...this._sessions.entries()].map(([connectId, sessionId]) => ({
631
641
  connectId,
@@ -640,6 +650,30 @@ var _LedgerAdapter = class _LedgerAdapter {
640
650
  serial: info.serialNumber
641
651
  }));
642
652
  }
653
+ // Enforce USB single-session invariant — see connectDevice.
654
+ async _evictAllSessions(reason) {
655
+ if (this._sessions.size === 0) return;
656
+ const stale = [...this._sessions.entries()];
657
+ debugLog(
658
+ "[SESS-DBG] evictAllSessions reason=",
659
+ reason,
660
+ "evicting=",
661
+ stale.map(([cid, sid]) => ({ connectId: cid, sessionId: sid }))
662
+ );
663
+ this._sessions.clear();
664
+ for (const [, sid] of stale) {
665
+ try {
666
+ await this.connector.disconnect(sid);
667
+ } catch (err) {
668
+ debugLog(
669
+ "[SESS-DBG] evictAllSessions disconnect failed sessionId=",
670
+ sid,
671
+ "err=",
672
+ err?.message
673
+ );
674
+ }
675
+ }
676
+ }
643
677
  static _createDeviceBusyError(method) {
644
678
  return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
645
679
  code: HardwareErrorCode2.DeviceBusy
@@ -660,6 +694,9 @@ var _LedgerAdapter = class _LedgerAdapter {
660
694
  code: HardwareErrorCode2.DeviceNotFound
661
695
  });
662
696
  }
697
+ if (!isLedgerBleConnectionType(this.connector.connectionType)) {
698
+ await this._evictAllSessions("connectDevice");
699
+ }
663
700
  await this._ensureDevicePermission(connectId);
664
701
  const session = await this.connector.connect(connectId);
665
702
  const prev = this._sessions.get(connectId);
@@ -1081,13 +1118,24 @@ var _LedgerAdapter = class _LedgerAdapter {
1081
1118
  return connectId;
1082
1119
  }
1083
1120
  if (!connectId && this._sessions.size > 0) {
1121
+ if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
1122
+ debugLog(
1123
+ "[SESS-DBG] ensureConnected ABORT multiple USB sessions present sessions=",
1124
+ this._snapshotSessions()
1125
+ );
1126
+ throw Object.assign(
1127
+ new Error(
1128
+ "Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
1129
+ ),
1130
+ { code: HardwareErrorCode2.DeviceOneDeviceOnly }
1131
+ );
1132
+ }
1084
1133
  const ambient = this._sessions.keys().next().value;
1085
1134
  debugLog(
1086
1135
  "[SESS-DBG] ensureConnected RESOLVED path=ambient-fallback chosenConnectId=",
1087
1136
  ambient,
1088
1137
  "sessionId=",
1089
- this._sessions.get(ambient),
1090
- "WARNING: caller passed empty connectId, routing to first session entry"
1138
+ this._sessions.get(ambient)
1091
1139
  );
1092
1140
  return ambient;
1093
1141
  }
@@ -1651,7 +1699,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1651
1699
  const tag = err && typeof err === "object" ? err._tag : void 0;
1652
1700
  if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
1653
1701
  const e = err;
1654
- const params = e.code === HardwareErrorCode2.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : void 0;
1702
+ const params = e.code === HardwareErrorCode2.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
1655
1703
  return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
1656
1704
  }
1657
1705
  const mapped = mapLedgerError(err);
@@ -1701,7 +1749,7 @@ _LedgerAdapter.MAX_DOCONNECT_CONFIRMS = 3;
1701
1749
  var LedgerAdapter = _LedgerAdapter;
1702
1750
 
1703
1751
  // src/connector/LedgerConnectorBase.ts
1704
- import { HardwareErrorCode as HardwareErrorCode7 } from "@onekeyfe/hwk-adapter-core";
1752
+ import { HardwareErrorCode as HardwareErrorCode8 } from "@onekeyfe/hwk-adapter-core";
1705
1753
 
1706
1754
  // src/device/LedgerDeviceManager.ts
1707
1755
  var LedgerDeviceManager = class {
@@ -3122,6 +3170,7 @@ async function _createTrx(ctx, sessionId) {
3122
3170
 
3123
3171
  // src/device-apps/DeviceApps.ts
3124
3172
  import { DeviceActionStatus as DeviceActionStatus2 } from "@ledgerhq/device-management-kit";
3173
+ import { HardwareErrorCode as HardwareErrorCode7 } from "@onekeyfe/hwk-adapter-core";
3125
3174
  import { Subject } from "rxjs";
3126
3175
  var INSTALL_TIMEOUT_MS = 5 * 6e4;
3127
3176
  var DeviceApps = class {
@@ -3187,6 +3236,72 @@ var DeviceApps = class {
3187
3236
  hwVersion: v.hwVersion
3188
3237
  };
3189
3238
  }
3239
+ /**
3240
+ * Pre-flight space check. Mirrors DMK's PredictOutOfMemoryTask using the
3241
+ * data we already expose (listInstalled / listAvailable / getOsVersion).
3242
+ * Fails fast with HardwareErrorCode.DeviceOutOfMemory before issuing the
3243
+ * actual install — saves the user the time-to-fail of DMK's state machine.
3244
+ */
3245
+ async _assertEnoughSpace(appName) {
3246
+ let osVersion;
3247
+ let installed;
3248
+ let available;
3249
+ try {
3250
+ [osVersion, installed, available] = await Promise.all([
3251
+ this._fetchOsVersion(),
3252
+ this.listInstalled(),
3253
+ this.listAvailable()
3254
+ ]);
3255
+ } catch (err) {
3256
+ debugLog("[DeviceApps] precheck skipped:", err?.message);
3257
+ return;
3258
+ }
3259
+ if (installed.some((a) => a.versionName === appName)) return;
3260
+ const target = available.find((a) => a.versionName === appName);
3261
+ if (!target) return;
3262
+ const capacity = getDeviceCapacity(osVersion.targetId);
3263
+ if (!capacity) return;
3264
+ const blockSize = capacity.getBlockSize(osVersion.seVersion);
3265
+ const totalBlocks = Math.floor(capacity.memorySize / blockSize);
3266
+ const blocks = (b) => Math.ceil((b ?? 0) / blockSize);
3267
+ const usedBlocks = installed.reduce((sum, a) => sum + blocks(a.bytes), 0);
3268
+ const neededBlocks = blocks(target.bytes);
3269
+ debugLog(
3270
+ "[DeviceApps] space check",
3271
+ "targetId=",
3272
+ osVersion.targetId.toString(16),
3273
+ "memorySize=",
3274
+ capacity.memorySize,
3275
+ "blockSize=",
3276
+ blockSize,
3277
+ "usedBlocks=",
3278
+ usedBlocks,
3279
+ "neededBlocks=",
3280
+ neededBlocks,
3281
+ "totalBlocks=",
3282
+ totalBlocks
3283
+ );
3284
+ if (usedBlocks + neededBlocks > totalBlocks) {
3285
+ const usedBytes = usedBlocks * blockSize;
3286
+ const neededBytes = neededBlocks * blockSize;
3287
+ const freeBytes = Math.max(0, capacity.memorySize - usedBytes);
3288
+ throw Object.assign(
3289
+ new Error(
3290
+ `Not enough space to install "${appName}": needs ${neededBytes} bytes, ${freeBytes} bytes free of ${capacity.memorySize}.`
3291
+ ),
3292
+ {
3293
+ code: HardwareErrorCode7.DeviceOutOfMemory,
3294
+ _tag: "OutOfMemoryDAError",
3295
+ appName,
3296
+ params: {
3297
+ requiredBytes: neededBytes,
3298
+ availableBytes: freeBytes,
3299
+ totalBytes: capacity.memorySize
3300
+ }
3301
+ }
3302
+ );
3303
+ }
3304
+ }
3190
3305
  // Sole sendCommand wrapper — surfaces unlock-device interaction through
3191
3306
  // the same onInteraction pipeline as the device-action methods.
3192
3307
  async _fetchOsVersion() {
@@ -3208,6 +3323,7 @@ var DeviceApps = class {
3208
3323
  async install(appName, onProgress, options) {
3209
3324
  if (!appName) throw new Error("DeviceApps.install: appName is required");
3210
3325
  debugLog("[DeviceApps] install:", appName);
3326
+ await this._assertEnoughSpace(appName);
3211
3327
  const action = this._dmk.executeDeviceAction({
3212
3328
  sessionId: this._sessionId,
3213
3329
  deviceAction: new this._ledgerKit.InstallAppDeviceAction({
@@ -3248,6 +3364,34 @@ function applicationToMetadata(app) {
3248
3364
  isDevTools: app.isDevTools
3249
3365
  };
3250
3366
  }
3367
+ var DEVICE_CAPACITIES = [
3368
+ // Nano S — block size 4K on fw<2.0, 2K on >=2.0. Default 4K (conservative).
3369
+ {
3370
+ mask: 823132160,
3371
+ capacity: {
3372
+ memorySize: 320 * 1024,
3373
+ getBlockSize: (fw) => parseMajor(fw) >= 2 ? 2 * 1024 : 4 * 1024
3374
+ }
3375
+ },
3376
+ // Nano S Plus
3377
+ { mask: 856686592, capacity: { memorySize: 1533 * 1024, getBlockSize: () => 32 } },
3378
+ // Nano X
3379
+ { mask: 855638016, capacity: { memorySize: 2 * 1024 * 1024, getBlockSize: () => 4 * 1024 } },
3380
+ // Stax
3381
+ { mask: 857735168, capacity: { memorySize: 1533 * 1024, getBlockSize: () => 32 } },
3382
+ // Flex
3383
+ { mask: 858783744, capacity: { memorySize: 1533 * 1024, getBlockSize: () => 32 } },
3384
+ // Apex / Nano Gen5
3385
+ { mask: 859832320, capacity: { memorySize: 1533 * 1024, getBlockSize: () => 32 } }
3386
+ ];
3387
+ function getDeviceCapacity(targetId) {
3388
+ const masked = targetId & 4294901760;
3389
+ return DEVICE_CAPACITIES.find((d) => d.mask === masked)?.capacity;
3390
+ }
3391
+ function parseMajor(version) {
3392
+ const m = /^(\d+)/.exec(version);
3393
+ return m ? Number(m[1]) : 0;
3394
+ }
3251
3395
  var GetOsVersionDeviceAction = class {
3252
3396
  constructor(_deps) {
3253
3397
  this._deps = _deps;
@@ -3362,7 +3506,7 @@ var METHOD_PREFIX_TO_APP_NAME = {
3362
3506
  tron: "Tron"
3363
3507
  };
3364
3508
  var HARDWARE_ERROR_CODE_VALUES = new Set(
3365
- Object.values(HardwareErrorCode7).filter((value) => typeof value === "number")
3509
+ Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
3366
3510
  );
3367
3511
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
3368
3512
  async function defaultLedgerKitImporter(pkg) {
@@ -3541,7 +3685,7 @@ var LedgerConnectorBase = class {
3541
3685
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
3542
3686
  );
3543
3687
  err._tag = ERROR_TAG.BlePairingTimeout;
3544
- err.code = HardwareErrorCode7.BlePairingTimeout;
3688
+ err.code = HardwareErrorCode8.BlePairingTimeout;
3545
3689
  reject(err);
3546
3690
  }, HANG_CEILING_MS);
3547
3691
  });
@@ -3574,7 +3718,7 @@ var LedgerConnectorBase = class {
3574
3718
  "Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
3575
3719
  );
3576
3720
  err._tag = ERROR_TAG.DeviceNotAdvertising;
3577
- err.code = HardwareErrorCode7.DeviceNotFound;
3721
+ err.code = HardwareErrorCode8.DeviceNotFound;
3578
3722
  throw err;
3579
3723
  };
3580
3724
  const doConnect = async (path) => {
@@ -3653,7 +3797,7 @@ var LedgerConnectorBase = class {
3653
3797
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
3654
3798
  );
3655
3799
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
3656
- wrapped.code = HardwareErrorCode7.BlePairingTimeout;
3800
+ wrapped.code = HardwareErrorCode8.BlePairingTimeout;
3657
3801
  wrapped.originalError = err;
3658
3802
  throw wrapped;
3659
3803
  }
@@ -3774,7 +3918,7 @@ var LedgerConnectorBase = class {
3774
3918
  this._unwatchSessionState(sessionId);
3775
3919
  this._signerManager?.invalidate(sessionId);
3776
3920
  this._cancellers.get(sessionId)?.({
3777
- code: HardwareErrorCode7.DeviceDisconnected,
3921
+ code: HardwareErrorCode8.DeviceDisconnected,
3778
3922
  tag: "DeviceDisconnected",
3779
3923
  message: "Device disconnected"
3780
3924
  });
@@ -3791,14 +3935,14 @@ var LedgerConnectorBase = class {
3791
3935
  } catch (err) {
3792
3936
  if (isAppStuckByApdu(err)) {
3793
3937
  throw Object.assign(new Error("Ledger app is unresponsive"), {
3794
- code: HardwareErrorCode7.DeviceAppStuck,
3938
+ code: HardwareErrorCode8.DeviceAppStuck,
3795
3939
  _tag: ERROR_TAG.DeviceAppStuck,
3796
3940
  originalError: err
3797
3941
  });
3798
3942
  }
3799
3943
  if (isTransportStuck(err)) {
3800
3944
  throw Object.assign(new Error("Device communication interrupted, please retry"), {
3801
- code: HardwareErrorCode7.TransportError,
3945
+ code: HardwareErrorCode8.TransportError,
3802
3946
  _tag: ERROR_TAG.DeviceTransportStuck,
3803
3947
  originalError: err
3804
3948
  });