@onekeyfe/hwk-ledger-adapter 1.1.26-patch.2 → 1.1.27-alpha.1

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
@@ -16,12 +16,6 @@ import {
16
16
 
17
17
  // src/errors.ts
18
18
  import { HardwareErrorCode, enrichErrorMessage } from "@onekeyfe/hwk-adapter-core";
19
- var MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE = "Multiple Ledger USB devices are connected. Please connect only one Ledger device and try again.";
20
- function createMultipleUsbLedgerDevicesError() {
21
- return Object.assign(new Error(MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE), {
22
- code: HardwareErrorCode.DeviceOneDeviceOnly
23
- });
24
- }
25
19
  function ledgerFailure(code, error, appName, tag, params) {
26
20
  const payload = { error, code };
27
21
  if (appName !== void 0) payload.appName = appName;
@@ -453,7 +447,7 @@ function btcAccountIndexFromPath(path) {
453
447
  return Number.isFinite(accountIndex) ? accountIndex : null;
454
448
  }
455
449
  var _LedgerAdapter = class _LedgerAdapter {
456
- constructor(connector) {
450
+ constructor(connector, options) {
457
451
  this.vendor = "ledger";
458
452
  this.emitter = new TypedEventEmitter();
459
453
  this._discoveredDevices = /* @__PURE__ */ new Map();
@@ -475,7 +469,7 @@ var _LedgerAdapter = class _LedgerAdapter {
475
469
  *
476
470
  * - If a session already exists for the given connectId, reuse it.
477
471
  * - If ANY session exists (Ledger IDs are ephemeral), reuse it.
478
- * - Otherwise: search → exactly 1 USB device auto-connects; multiple or none throws.
472
+ * - Otherwise: search → 1 device: auto-connect, multiple: ask user, 0: throw.
479
473
  */
480
474
  // Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
481
475
  this._connectingPromise = null;
@@ -506,6 +500,7 @@ var _LedgerAdapter = class _LedgerAdapter {
506
500
  this.emitter.emit("ui-event", event);
507
501
  };
508
502
  this.connector = connector;
503
+ this._handleSelectDevice = options?.handleSelectDevice ?? false;
509
504
  this._jobQueue = new DeviceJobQueue();
510
505
  this.registerEventListeners();
511
506
  }
@@ -579,18 +574,6 @@ var _LedgerAdapter = class _LedgerAdapter {
579
574
  );
580
575
  return Array.from(this._discoveredDevices.values());
581
576
  }
582
- // USB single-session invariant: evict all sessions, best-effort (see connectDevice).
583
- async _evictAllSessions() {
584
- if (this._sessions.size === 0) return;
585
- const stale = [...this._sessions.values()];
586
- this._sessions.clear();
587
- for (const sid of stale) {
588
- try {
589
- await this.connector.disconnect(sid);
590
- } catch {
591
- }
592
- }
593
- }
594
577
  static _createDeviceBusyError(method) {
595
578
  return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
596
579
  code: HardwareErrorCode2.DeviceBusy
@@ -603,9 +586,6 @@ var _LedgerAdapter = class _LedgerAdapter {
603
586
  code: HardwareErrorCode2.DeviceNotFound
604
587
  });
605
588
  }
606
- if (!isLedgerBleConnectionType(this.connector.connectionType)) {
607
- await this._evictAllSessions();
608
- }
609
589
  await this._ensureDevicePermission(connectId);
610
590
  const session = await this.connector.connect(connectId);
611
591
  this._sessions.set(connectId, session.sessionId);
@@ -933,15 +913,7 @@ var _LedgerAdapter = class _LedgerAdapter {
933
913
  });
934
914
  }
935
915
  if (connectId && this._sessions.has(connectId)) return connectId;
936
- if (!connectId && this._sessions.size > 0) {
937
- if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
938
- throw Object.assign(
939
- new Error(
940
- "Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
941
- ),
942
- { code: HardwareErrorCode2.DeviceOneDeviceOnly }
943
- );
944
- }
916
+ if (this._sessions.size > 0) {
945
917
  return this._sessions.keys().next().value;
946
918
  }
947
919
  if (!this._connectingPromise) {
@@ -1029,37 +1001,24 @@ var _LedgerAdapter = class _LedgerAdapter {
1029
1001
  const target = devices.find(
1030
1002
  (d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
1031
1003
  );
1032
- if (target) {
1033
- return this._connectDeviceOrThrow(target.connectId);
1034
- }
1035
- if (!isLedgerBleConnectionType(this.connector.connectionType) && devices.length === 1) {
1036
- debugLog(
1037
- `[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} (assumed ephemeral path change)`
1038
- );
1039
- return this._connectDeviceOrThrow(devices[0].connectId);
1040
- }
1041
- const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
1042
- code: HardwareErrorCode2.DeviceNotFound
1043
- });
1044
- if (isLedgerBleConnectionType(this.connector.connectionType)) {
1045
- err._tag = ERROR_TAG.DeviceNotAdvertising;
1004
+ if (!target) {
1005
+ const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
1006
+ code: HardwareErrorCode2.DeviceNotFound
1007
+ });
1008
+ if (isLedgerBleConnectionType(this.connector.connectionType)) {
1009
+ err._tag = ERROR_TAG.DeviceNotAdvertising;
1010
+ }
1011
+ throw err;
1046
1012
  }
1047
- throw err;
1013
+ return this._connectDeviceOrThrow(target.connectId);
1048
1014
  }
1049
1015
  if (isLedgerBleConnectionType(this.connector.connectionType)) {
1050
1016
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
1051
1017
  code: HardwareErrorCode2.DeviceNotFound
1052
1018
  });
1053
1019
  }
1054
- if (devices.length > 1) {
1055
- throw createMultipleUsbLedgerDevicesError();
1056
- }
1057
- if (devices.length !== 1) {
1058
- throw Object.assign(new Error("Ledger device not found."), {
1059
- code: HardwareErrorCode2.DeviceNotFound
1060
- });
1061
- }
1062
- return this._connectDeviceOrThrow(devices[0].connectId);
1020
+ const chosenConnectId = devices.length === 1 ? devices[0].connectId : await this._chooseDeviceFromList(devices);
1021
+ return this._connectDeviceOrThrow(chosenConnectId);
1063
1022
  }
1064
1023
  async _connectDeviceOrThrow(chosenConnectId) {
1065
1024
  const result = await this.connectDevice(chosenConnectId);
@@ -1075,6 +1034,46 @@ var _LedgerAdapter = class _LedgerAdapter {
1075
1034
  }
1076
1035
  return chosenConnectId;
1077
1036
  }
1037
+ async _chooseDeviceFromList(devices) {
1038
+ if (!this._handleSelectDevice) {
1039
+ debugLog(
1040
+ `[DMK] Multiple Ledger devices found (${devices.length}); handleSelectDevice=false, picking first (${devices[0].connectId}).`
1041
+ );
1042
+ return devices[0].connectId;
1043
+ }
1044
+ let response;
1045
+ try {
1046
+ const waitPromise = this._uiRegistry.wait(
1047
+ UI_REQUEST.REQUEST_SELECT_DEVICE
1048
+ );
1049
+ this.emitter.emit(UI_REQUEST.REQUEST_SELECT_DEVICE, {
1050
+ type: UI_REQUEST.REQUEST_SELECT_DEVICE,
1051
+ payload: { devices }
1052
+ });
1053
+ response = await waitPromise;
1054
+ } catch (err) {
1055
+ if (err?._tag === UI_REQUEST_PREEMPTED_TAG) {
1056
+ this.emitter.emit(UI_REQUEST.CLOSE_UI_WINDOW, {
1057
+ type: UI_REQUEST.CLOSE_UI_WINDOW,
1058
+ payload: {}
1059
+ });
1060
+ throw Object.assign(new Error("Device selection superseded"), {
1061
+ _tag: ERROR_TAG.UserAborted,
1062
+ code: HardwareErrorCode2.UserAborted,
1063
+ cause: err
1064
+ });
1065
+ }
1066
+ throw err;
1067
+ }
1068
+ const chosen = devices.find((d) => d.connectId === response?.sdkConnectId);
1069
+ if (!chosen) {
1070
+ throw Object.assign(
1071
+ new Error(`Selected sdkConnectId '${response?.sdkConnectId}' not in discovered list`),
1072
+ { _tag: ERROR_TAG.DeviceNotRecognized }
1073
+ );
1074
+ }
1075
+ return chosen.connectId;
1076
+ }
1078
1077
  /**
1079
1078
  * Call the connector with automatic session resolution and disconnect retry.
1080
1079
  *
@@ -1239,7 +1238,8 @@ var _LedgerAdapter = class _LedgerAdapter {
1239
1238
  } catch {
1240
1239
  }
1241
1240
  }
1242
- const reConnectId = await this.ensureConnected(resolvedConnectId, signal);
1241
+ const retryConnectId = isLedgerBleConnectionType(this.connector.connectionType) ? resolvedConnectId : void 0;
1242
+ const reConnectId = await this.ensureConnected(retryConnectId, signal);
1243
1243
  const reSessionId = this._sessions.get(reConnectId);
1244
1244
  if (!reSessionId) throw lastErr;
1245
1245
  if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
@@ -1307,7 +1307,8 @@ var _LedgerAdapter = class _LedgerAdapter {
1307
1307
  */
1308
1308
  async _retryAfterStuckApp(resolvedConnectId, method, params, signal, originalErr, fingerprint) {
1309
1309
  await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
1310
- const retryConnectId = await this.ensureConnected(resolvedConnectId, signal);
1310
+ const retryTargetConnectId = isLedgerBleConnectionType(this.connector.connectionType) ? resolvedConnectId : void 0;
1311
+ const retryConnectId = await this.ensureConnected(retryTargetConnectId, signal);
1311
1312
  const retrySessionId = this._sessions.get(retryConnectId);
1312
1313
  if (!retrySessionId) throw originalErr;
1313
1314
  if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
@@ -3051,12 +3052,6 @@ var LedgerConnectorBase = class {
3051
3052
  }
3052
3053
  // ---------------------------------------------------------------------------
3053
3054
  // IConnector -- Device discovery
3054
- //
3055
- // Discovery never throws on multiple USB devices — it returns the full list.
3056
- // The "connect exactly one device" rule is enforced upstream: an explicit
3057
- // connectId connects that device (or fails as not-found), and only the
3058
- // no-connectId USB path rejects when more than one device is present
3059
- // (see LedgerAdapter._connectFirstOrSelect).
3060
3055
  // ---------------------------------------------------------------------------
3061
3056
  async searchDevices() {
3062
3057
  const dm = await this._getDeviceManager();
@@ -3162,16 +3157,7 @@ var LedgerConnectorBase = class {
3162
3157
  }
3163
3158
  throw err;
3164
3159
  }
3165
- try {
3166
- this._watchSessionState(sessionId, externalConnectId);
3167
- } catch (subErr) {
3168
- debugLog("[DMK] state subscription failed during connect; disconnecting session:", subErr);
3169
- try {
3170
- await dm.disconnect(sessionId);
3171
- } catch {
3172
- }
3173
- throw subErr;
3174
- }
3160
+ this._watchSessionState(sessionId, externalConnectId);
3175
3161
  const info = dm.getDiscoveredDeviceInfo(path);
3176
3162
  const session = {
3177
3163
  sessionId,
@@ -3237,7 +3223,8 @@ var LedgerConnectorBase = class {
3237
3223
  * `LedgerAdapter._sessions` map would hold a dead session entry until
3238
3224
  * the next call hit `DeviceSessionNotFound`.
3239
3225
  *
3240
- * Subscribe failure is fatal see the inline note at the subscribe call.
3226
+ * Best-effort: any error subscribing is swallowed so a flaky DMK
3227
+ * doesn't break the connect path.
3241
3228
  */
3242
3229
  _watchSessionState(sessionId, externalConnectId) {
3243
3230
  const dmk = this._dmk;
@@ -3249,20 +3236,24 @@ var LedgerConnectorBase = class {
3249
3236
  } catch {
3250
3237
  }
3251
3238
  }
3252
- const sub = dmk.getDeviceSessionState({ sessionId }).subscribe({
3253
- next: (state) => {
3254
- if (state?.deviceStatus === "NOT CONNECTED") {
3239
+ try {
3240
+ const sub = dmk.getDeviceSessionState({ sessionId }).subscribe({
3241
+ next: (state) => {
3242
+ if (state?.deviceStatus === "NOT CONNECTED") {
3243
+ this._handleAutonomousDisconnect(sessionId, externalConnectId);
3244
+ }
3245
+ },
3246
+ error: () => {
3247
+ this._handleAutonomousDisconnect(sessionId, externalConnectId);
3248
+ },
3249
+ complete: () => {
3255
3250
  this._handleAutonomousDisconnect(sessionId, externalConnectId);
3256
3251
  }
3257
- },
3258
- error: () => {
3259
- this._handleAutonomousDisconnect(sessionId, externalConnectId);
3260
- },
3261
- complete: () => {
3262
- this._handleAutonomousDisconnect(sessionId, externalConnectId);
3263
- }
3264
- });
3265
- this._sessionStateSubs.set(sessionId, sub);
3252
+ });
3253
+ this._sessionStateSubs.set(sessionId, sub);
3254
+ } catch (err) {
3255
+ debugLog("[DMK] _watchSessionState subscribe failed:", err);
3256
+ }
3266
3257
  }
3267
3258
  _unwatchSessionState(sessionId) {
3268
3259
  const sub = this._sessionStateSubs.get(sessionId);