@onekeyfe/hwk-ledger-adapter 1.2.0-alpha.0 → 1.2.0-alpha.2

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
@@ -4,15 +4,16 @@ import {
4
4
  DEVICE,
5
5
  DeviceJobQueue,
6
6
  EConnectorInteraction,
7
- HardwareErrorCode as HardwareErrorCode2,
7
+ HardwareErrorCode as HardwareErrorCode3,
8
8
  TypedEventEmitter,
9
9
  UI_REQUEST,
10
10
  UI_REQUEST_PREEMPTED_TAG,
11
11
  UiRequestRegistry,
12
+ createHwkError,
12
13
  deriveDeviceFingerprint,
13
- failure,
14
+ failure as failure2,
14
15
  rehydrateConnectorError,
15
- success
16
+ success as success2
16
17
  } from "@onekeyfe/hwk-adapter-core";
17
18
 
18
19
  // src/errors.ts
@@ -35,30 +36,37 @@ var USER_REJECTED_CODES = /* @__PURE__ */ new Set(["6985", "27013"]);
35
36
  var WRONG_APP_CODES = /* @__PURE__ */ new Set(["6e00", "28160", "6d00", "27904", "6a83", "27267"]);
36
37
  var APP_NOT_INSTALLED_CODES = /* @__PURE__ */ new Set(["6807", "26631"]);
37
38
  var STEP_BLIND_SIGN_TRANSACTION_FALLBACK = "signer.eth.steps.blindSignTransactionFallback";
39
+ var STEP_DETECT_BLIND_SIGNING = "signer.eth.steps.detectBlindSigning";
40
+ var BLIND_SIGNING_STEPS = /* @__PURE__ */ new Set([
41
+ STEP_BLIND_SIGN_TRANSACTION_FALLBACK,
42
+ STEP_DETECT_BLIND_SIGNING
43
+ ]);
44
+ function normalizeApduHex(value) {
45
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 65535) {
46
+ return value.toString(16).padStart(4, "0");
47
+ }
48
+ if (typeof value !== "string") return null;
49
+ const raw = value.toLowerCase().replace(/^0x/, "");
50
+ return /^[0-9a-f]{1,4}$/i.test(raw) ? raw.padStart(4, "0") : null;
51
+ }
38
52
  function getEthAppErrorCode(err) {
39
53
  if (!err || typeof err !== "object") return null;
40
54
  const e = err;
41
- if (e._tag === ERROR_TAG.EthAppCommand && typeof e.errorCode === "string") {
42
- return e.errorCode.toLowerCase();
55
+ if (e._tag === ERROR_TAG.EthAppCommand) {
56
+ const code = normalizeApduHex(e.errorCode) ?? normalizeApduHex(e.statusCode) ?? normalizeApduHex(e.code);
57
+ if (code) return code;
43
58
  }
44
59
  const orig = e.originalError;
45
- if (orig?._tag === ERROR_TAG.EthAppCommand && typeof orig.errorCode === "string") {
46
- return orig.errorCode.toLowerCase();
60
+ if (orig?._tag === ERROR_TAG.EthAppCommand) {
61
+ return normalizeApduHex(orig.errorCode) ?? normalizeApduHex(orig.statusCode) ?? normalizeApduHex(orig.code);
47
62
  }
48
63
  return null;
49
64
  }
50
65
  function extractApduHex(err) {
51
66
  if (!err || typeof err !== "object") return null;
52
67
  const e = err;
53
- if (typeof e.errorCode === "string" && /^[0-9a-f]+$/i.test(e.errorCode)) {
54
- return e.errorCode.toLowerCase();
55
- }
56
- if (typeof e.statusCode === "number" && Number.isFinite(e.statusCode)) {
57
- return e.statusCode.toString(16).padStart(4, "0");
58
- }
59
- if (typeof e.statusCode === "string" && /^[0-9a-f]+$/i.test(e.statusCode)) {
60
- return e.statusCode.toLowerCase();
61
- }
68
+ const direct = normalizeApduHex(e.errorCode) ?? normalizeApduHex(e.statusCode);
69
+ if (direct) return direct;
62
70
  if (e.originalError != null) {
63
71
  const nested = extractApduHex(e.originalError);
64
72
  if (nested) return nested;
@@ -117,10 +125,15 @@ function classifyEthAppError(err) {
117
125
  const ethCode = getEthAppErrorCode(err);
118
126
  if (!ethCode) return null;
119
127
  if (ethCode === "6a80") {
120
- return hasBlindSignFallbackStep(err) ? HardwareErrorCode.EvmBlindSigningRequired : null;
128
+ return hasBlindSigningStep(err) ? HardwareErrorCode.EvmBlindSigningRequired : null;
121
129
  }
122
130
  return mapEthAppErrorCode(ethCode);
123
131
  }
132
+ function classifyBlindSigningDetectionError(err) {
133
+ if (!hasBlindSigningStep(err)) return null;
134
+ if (hasInvalidArgumentCode(err)) return HardwareErrorCode.EvmBlindSigningRequired;
135
+ return null;
136
+ }
124
137
  var ERROR_TAG = {
125
138
  // SDK-mint
126
139
  DeviceNotAdvertising: "DeviceNotAdvertisingError",
@@ -136,6 +149,10 @@ var ERROR_TAG = {
136
149
  // chain app wedged (APDU 0x6901)
137
150
  DeviceTransportStuck: "DeviceTransportStuck",
138
151
  // DMK transport queue wedged
152
+ // installApp resolved success but the app is still missing on device —
153
+ // DMK quirk. Surfaced as a distinct tag so callers can tell it apart from
154
+ // the original DMK-thrown AppNotInstalled.
155
+ AppInstallVerifyFailed: "AppInstallVerifyFailedError",
139
156
  // DMK-reuse (DMK throws same string; we synthesize too)
140
157
  DeviceLocked: "DeviceLockedError",
141
158
  DeviceNotRecognized: "DeviceNotRecognizedError",
@@ -161,7 +178,11 @@ var ERROR_TAG = {
161
178
  // ble-plx surfaces this when GATT notification setup fails — typical
162
179
  // outcome when the user doesn't confirm pairing on the device, or the
163
180
  // existing bond is invalid. Observed in production after ~30s.
164
- PairingRefused: "PairingRefusedError"
181
+ PairingRefused: "PairingRefusedError",
182
+ // DMK remote-network failures (manager-api HTTP / secure-channel WS).
183
+ WebSocketConnection: "WebSocketConnectionError",
184
+ HttpFetch: "FetchError",
185
+ InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError"
165
186
  };
166
187
  function isDeviceLockedError(err) {
167
188
  if (!err || typeof err !== "object") return false;
@@ -249,15 +270,30 @@ function hasStatusCode(err, codeSet) {
249
270
  if (e.error != null && e._tag && hasStatusCode(e.error, codeSet)) return true;
250
271
  return false;
251
272
  }
252
- function hasBlindSignFallbackStep(err) {
273
+ function hasDeviceActionStep(err, step) {
253
274
  if (!err || typeof err !== "object") return false;
254
275
  const e = err;
255
- if (e._lastStep === STEP_BLIND_SIGN_TRANSACTION_FALLBACK) return true;
256
- if (Array.isArray(e._deviceActionSteps) && e._deviceActionSteps.includes(STEP_BLIND_SIGN_TRANSACTION_FALLBACK)) {
276
+ if (e._lastStep === step) return true;
277
+ if (Array.isArray(e._deviceActionSteps) && e._deviceActionSteps.includes(step)) {
257
278
  return true;
258
279
  }
259
- if (e.originalError != null && hasBlindSignFallbackStep(e.originalError)) return true;
260
- if (e.error != null && e._tag && hasBlindSignFallbackStep(e.error)) return true;
280
+ if (e.originalError != null && hasDeviceActionStep(e.originalError, step)) return true;
281
+ if (e.error != null && e._tag && hasDeviceActionStep(e.error, step)) return true;
282
+ return false;
283
+ }
284
+ function hasBlindSigningStep(err) {
285
+ for (const step of BLIND_SIGNING_STEPS) {
286
+ if (hasDeviceActionStep(err, step)) return true;
287
+ }
288
+ return false;
289
+ }
290
+ function hasInvalidArgumentCode(err) {
291
+ if (!err || typeof err !== "object") return false;
292
+ const e = err;
293
+ if (e.code === "INVALID_ARGUMENT") return true;
294
+ if (typeof e.message === "string" && e.message.includes("code=INVALID_ARGUMENT")) return true;
295
+ if (e.originalError != null && hasInvalidArgumentCode(e.originalError)) return true;
296
+ if (e.error != null && e._tag && hasInvalidArgumentCode(e.error)) return true;
261
297
  return false;
262
298
  }
263
299
  function isDeviceNotFoundError(err) {
@@ -286,6 +322,11 @@ function isUserRejectedError(err) {
286
322
  if (hasStatusCode(err, USER_REJECTED_CODES)) return true;
287
323
  return false;
288
324
  }
325
+ function isUserAbortedError(err) {
326
+ if (!err || typeof err !== "object") return false;
327
+ const e = err;
328
+ return e._tag === ERROR_TAG.UserAborted;
329
+ }
289
330
  function isWrongAppError(err) {
290
331
  if (!err || typeof err !== "object") return false;
291
332
  const e = err;
@@ -312,6 +353,20 @@ function isOutOfMemoryError(err) {
312
353
  const e = err;
313
354
  return e._tag === "OutOfMemoryDAError";
314
355
  }
356
+ var NETWORK_ERROR_TAGS = /* @__PURE__ */ new Set([
357
+ ERROR_TAG.WebSocketConnection,
358
+ ERROR_TAG.HttpFetch,
359
+ ERROR_TAG.InvalidFirmwareMetadataResponse
360
+ ]);
361
+ function isNetworkError(err) {
362
+ if (!err || typeof err !== "object") return false;
363
+ const e = err;
364
+ const tag = e._tag;
365
+ if (typeof tag === "string" && NETWORK_ERROR_TAGS.has(tag)) return true;
366
+ if (e.originalError != null && isNetworkError(e.originalError)) return true;
367
+ if (e.error != null && isNetworkError(e.error)) return true;
368
+ return false;
369
+ }
315
370
  function isDeviceDisconnectedError(err) {
316
371
  if (!err || typeof err !== "object") return false;
317
372
  const e = err;
@@ -368,6 +423,8 @@ function mapLedgerError(err, opts) {
368
423
  code = HardwareErrorCode.DeviceBusy;
369
424
  } else if (isBlePairingFailureError(err)) {
370
425
  code = HardwareErrorCode.BlePairingTimeout;
426
+ } else if (isUserAbortedError(err)) {
427
+ code = HardwareErrorCode.UserAborted;
371
428
  } else if (isUserRejectedError(err)) {
372
429
  code = HardwareErrorCode.UserRejected;
373
430
  } else if (isWrongAppError(err)) {
@@ -376,12 +433,14 @@ function mapLedgerError(err, opts) {
376
433
  code = HardwareErrorCode.AppNotInstalled;
377
434
  } else if (isOutOfMemoryError(err)) {
378
435
  code = HardwareErrorCode.DeviceOutOfMemory;
436
+ } else if (isNetworkError(err)) {
437
+ code = HardwareErrorCode.NetworkError;
379
438
  } else if (isDeviceDisconnectedError(err)) {
380
439
  code = HardwareErrorCode.DeviceDisconnected;
381
440
  } else if (isTimeoutError(err)) {
382
441
  code = HardwareErrorCode.OperationTimeout;
383
442
  } else {
384
- const ethMapped = classifyEthAppError(err);
443
+ const ethMapped = classifyEthAppError(err) ?? classifyBlindSigningDetectionError(err);
385
444
  const apduHex = ethMapped ? null : extractApduHex(err);
386
445
  const chainMapped = apduHex ? mapSolanaAppError(apduHex) ?? mapTronAppError(apduHex) ?? mapBtcAppError(apduHex) : null;
387
446
  code = ethMapped ?? chainMapped ?? HardwareErrorCode.UnknownError;
@@ -391,17 +450,8 @@ function mapLedgerError(err, opts) {
391
450
  return { code, message: enrichErrorMessage(code, originalMessage), appName };
392
451
  }
393
452
 
394
- // src/utils/ledgerDmkTransport.ts
395
- function isLedgerDmkBleTransport(transport) {
396
- const normalized = transport?.toUpperCase();
397
- return normalized === "BLE" || normalized?.endsWith("_BLE") === true;
398
- }
399
- function isLedgerBleConnectionType(connectionType) {
400
- return connectionType === "ble";
401
- }
402
- function isLedgerBleDescriptor(connectionType, descriptor) {
403
- return isLedgerBleConnectionType(connectionType) || isLedgerDmkBleTransport(descriptor.transport);
404
- }
453
+ // src/adapter/methods/allNetworkGetAddress.ts
454
+ import { HardwareErrorCode as HardwareErrorCode2, failure, success } from "@onekeyfe/hwk-adapter-core";
405
455
 
406
456
  // src/utils/sdkEventBus.ts
407
457
  var listeners = /* @__PURE__ */ new Set();
@@ -448,6 +498,252 @@ function debugError(...args) {
448
498
  emitLog("error", ...args);
449
499
  }
450
500
 
501
+ // src/adapter/methods/allNetworkGetAddress.ts
502
+ var LEDGER_BTC_NETWORK_COIN_MAP = {
503
+ tbtc: "Testnet",
504
+ bch: "Bcash",
505
+ doge: "Dogecoin",
506
+ ltc: "Litecoin",
507
+ neurai: "Neurai"
508
+ };
509
+ function createAllNetworkGetAddress({
510
+ callChain,
511
+ getChainFingerprint
512
+ }) {
513
+ return async function allNetworkGetAddress(connectId, _deviceId, params) {
514
+ debugLog("[LedgerAdapter][REQ]", { method: "allNetworkGetAddress", connectId, params });
515
+ const installContext = {};
516
+ const commonParams = {
517
+ autoInstallApp: params.autoInstallApp
518
+ };
519
+ const responses = [];
520
+ const chainFingerprints = /* @__PURE__ */ new Map();
521
+ for (const item of params.bundle) {
522
+ const method = getAllNetworkMethod(item);
523
+ if (!method) {
524
+ responses.push(buildUnsupportedMethodResponse(item));
525
+ } else {
526
+ const chain = getFingerprintChain(method);
527
+ const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
528
+ const normalizedItem = normalizeLedgerAllNetworkItem(method, item);
529
+ const response = await callAllNetworkItem(
530
+ callChain,
531
+ getChainFingerprint,
532
+ connectId,
533
+ itemDeviceId,
534
+ chain,
535
+ method,
536
+ normalizedItem,
537
+ commonParams,
538
+ installContext,
539
+ chainFingerprints
540
+ );
541
+ if (isTopLevelAllNetworkFailure(response)) {
542
+ const code = response.payload?.code ?? HardwareErrorCode2.DeviceMismatch;
543
+ const result2 = failure(
544
+ code,
545
+ response.payload?.error ?? "All-network get-address aborted",
546
+ response.payload?.params
547
+ );
548
+ debugLog("[LedgerAdapter][RES]", {
549
+ method: "allNetworkGetAddress",
550
+ success: false,
551
+ payload: result2
552
+ });
553
+ return result2;
554
+ }
555
+ responses.push(response);
556
+ }
557
+ }
558
+ const result = success(responses);
559
+ debugLog("[LedgerAdapter][RES]", {
560
+ method: "allNetworkGetAddress",
561
+ success: true,
562
+ payload: result
563
+ });
564
+ return result;
565
+ };
566
+ }
567
+ function isTopLevelAllNetworkFailure(response) {
568
+ if (response.success) {
569
+ return false;
570
+ }
571
+ const code = response.payload?.code;
572
+ return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
573
+ }
574
+ function getItemDeviceId(item) {
575
+ const { deviceId } = item;
576
+ return typeof deviceId === "string" && deviceId.length > 0 ? deviceId : void 0;
577
+ }
578
+ function getAllNetworkMethod(item) {
579
+ switch (item.methodName) {
580
+ case "evmGetAddress":
581
+ case "btcGetAddress":
582
+ case "btcGetPublicKey":
583
+ case "solGetAddress":
584
+ case "tronGetAddress":
585
+ return item.methodName;
586
+ default:
587
+ return void 0;
588
+ }
589
+ }
590
+ function buildUnsupportedMethodResponse(item) {
591
+ return {
592
+ ...item,
593
+ success: false,
594
+ payload: {
595
+ code: HardwareErrorCode2.InvalidParams,
596
+ error: `Unsupported allNetwork method: ${String(item.methodName)}`
597
+ }
598
+ };
599
+ }
600
+ function normalizeLedgerAllNetworkItem(method, item) {
601
+ if (method !== "btcGetAddress" && method !== "btcGetPublicKey") {
602
+ return item;
603
+ }
604
+ const itemWithCoin = item;
605
+ if (itemWithCoin.coin) {
606
+ return item;
607
+ }
608
+ const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
609
+ return coin ? { ...item, coin } : item;
610
+ }
611
+ async function callAllNetworkItem(callChain, getChainFingerprint, connectId, deviceId, chain, method, item, commonParams, installContext, chainFingerprints) {
612
+ const response = await callAllNetworkMethod(
613
+ callChain,
614
+ connectId,
615
+ deviceId,
616
+ method,
617
+ item,
618
+ commonParams,
619
+ installContext
620
+ );
621
+ if (!response.success) {
622
+ return { ...item, success: false, payload: response.payload };
623
+ }
624
+ const payload = response.payload;
625
+ const fingerprint = deviceId || chainFingerprints.get(chain) || await bootstrapChainFingerprint(getChainFingerprint, connectId, chain);
626
+ if (!fingerprint) {
627
+ return buildFingerprintBootstrapFailure(item, chain);
628
+ }
629
+ chainFingerprints.set(chain, fingerprint);
630
+ return {
631
+ ...item,
632
+ success: true,
633
+ payload: {
634
+ ...payload,
635
+ chainFingerprint: fingerprint,
636
+ chainFingerprintChain: chain
637
+ }
638
+ };
639
+ }
640
+ async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
641
+ const response = await getChainFingerprint(connectId, chain);
642
+ return response.success ? response.payload : "";
643
+ }
644
+ function buildFingerprintBootstrapFailure(item, chain) {
645
+ return {
646
+ ...item,
647
+ success: false,
648
+ payload: {
649
+ code: HardwareErrorCode2.DeviceMismatch,
650
+ error: `Could not establish chain fingerprint for ${chain} after device call; refusing to return unverifiable result. Please retry.`
651
+ }
652
+ };
653
+ }
654
+ function getFingerprintChain(method) {
655
+ switch (method) {
656
+ case "evmGetAddress":
657
+ return "evm";
658
+ case "btcGetAddress":
659
+ case "btcGetPublicKey":
660
+ return "btc";
661
+ case "solGetAddress":
662
+ return "sol";
663
+ case "tronGetAddress":
664
+ return "tron";
665
+ default:
666
+ throw Object.assign(new Error(`Unsupported allNetwork method: ${method}`), {
667
+ code: HardwareErrorCode2.InvalidParams
668
+ });
669
+ }
670
+ }
671
+ async function callAllNetworkMethod(callChain, connectId, deviceId, method, item, commonParams, installContext) {
672
+ switch (method) {
673
+ case "evmGetAddress":
674
+ return callChain(
675
+ connectId,
676
+ deviceId,
677
+ "evm",
678
+ "evmGetAddress",
679
+ item,
680
+ commonParams,
681
+ false,
682
+ installContext
683
+ );
684
+ case "btcGetAddress":
685
+ return callChain(
686
+ connectId,
687
+ deviceId,
688
+ "btc",
689
+ "btcGetAddress",
690
+ item,
691
+ commonParams,
692
+ false,
693
+ installContext
694
+ );
695
+ case "btcGetPublicKey":
696
+ return callChain(
697
+ connectId,
698
+ deviceId,
699
+ "btc",
700
+ "btcGetPublicKey",
701
+ item,
702
+ commonParams,
703
+ false,
704
+ installContext
705
+ );
706
+ case "solGetAddress":
707
+ return callChain(
708
+ connectId,
709
+ deviceId,
710
+ "sol",
711
+ "solGetAddress",
712
+ item,
713
+ commonParams,
714
+ false,
715
+ installContext
716
+ );
717
+ case "tronGetAddress":
718
+ return callChain(
719
+ connectId,
720
+ deviceId,
721
+ "tron",
722
+ "tronGetAddress",
723
+ item,
724
+ commonParams,
725
+ false,
726
+ installContext
727
+ );
728
+ default:
729
+ throw Object.assign(new Error(`Unsupported allNetwork method: ${method}`), {
730
+ code: HardwareErrorCode2.InvalidParams
731
+ });
732
+ }
733
+ }
734
+
735
+ // src/utils/ledgerDmkTransport.ts
736
+ function isLedgerDmkBleTransport(transport) {
737
+ const normalized = transport?.toUpperCase();
738
+ return normalized === "BLE" || normalized?.endsWith("_BLE") === true;
739
+ }
740
+ function isLedgerBleConnectionType(connectionType) {
741
+ return connectionType === "ble";
742
+ }
743
+ function isLedgerBleDescriptor(connectionType, descriptor) {
744
+ return isLedgerBleConnectionType(connectionType) || isLedgerDmkBleTransport(descriptor.transport);
745
+ }
746
+
451
747
  // src/adapter/LedgerAdapter.ts
452
748
  function formatDeviceMismatchError(expected, actual) {
453
749
  return `Wrong device: expected ${expected}, got ${actual}`;
@@ -475,6 +771,11 @@ var _LedgerAdapter = class _LedgerAdapter {
475
771
  this._btcHighIndexConfirmedThisSession = false;
476
772
  // Shared across concurrent callers — only `cancel()` aborts.
477
773
  this._doConnectAbortController = null;
774
+ this._installProgressLastEmittedValue = -Infinity;
775
+ this.allNetworkGetAddress = createAllNetworkGetAddress({
776
+ callChain: this.callChain.bind(this),
777
+ getChainFingerprint: (connectId, chain) => this.getChainFingerprint(connectId, "", chain)
778
+ });
478
779
  // ---------------------------------------------------------------------------
479
780
  // Private helpers
480
781
  // ---------------------------------------------------------------------------
@@ -528,12 +829,23 @@ var _LedgerAdapter = class _LedgerAdapter {
528
829
  );
529
830
  return;
530
831
  }
832
+ const { appName, progress } = event.payload;
833
+ const key = `${connectId}:${appName}`;
834
+ if (this._installProgressLastKey !== key || this._installProgressLastEmittedValue >= 1) {
835
+ this._installProgressLastEmittedValue = -Infinity;
836
+ this._installProgressLastKey = key;
837
+ }
838
+ const delta = progress - this._installProgressLastEmittedValue;
839
+ if (delta < _LedgerAdapter.APP_INSTALL_PROGRESS_MIN_DELTA && progress < 1) {
840
+ return;
841
+ }
842
+ this._installProgressLastEmittedValue = progress;
531
843
  this.emitter.emit("ui-event", {
532
844
  type: EConnectorInteraction.AppInstallProgress,
533
845
  payload: {
534
846
  connectId,
535
- appName: event.payload.appName,
536
- progress: event.payload.progress
847
+ appName,
848
+ progress
537
849
  }
538
850
  });
539
851
  return;
@@ -590,30 +902,42 @@ var _LedgerAdapter = class _LedgerAdapter {
590
902
  // Device management
591
903
  // ---------------------------------------------------------------------------
592
904
  async searchDevices(options) {
593
- if (options?.resetSession) {
594
- this._doConnectAbortController?.abort();
595
- this._sessions.clear();
596
- this._connectingPromise = null;
597
- this._doConnectAbortController = null;
598
- this._btcHighIndexConfirmedThisSession = false;
599
- }
600
- await this._ensureDevicePermission();
601
- const devices = await this.connector.searchDevices();
602
- this._discoveredDevices.clear();
603
- for (const d of devices) {
604
- if (d.connectId) {
605
- this._discoveredDevices.set(d.connectId, this.connectorDeviceToDeviceInfo(d));
905
+ debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
906
+ try {
907
+ if (options?.resetSession) {
908
+ this._doConnectAbortController?.abort();
909
+ this._sessions.clear();
910
+ this._connectingPromise = null;
911
+ this._doConnectAbortController = null;
912
+ this._btcHighIndexConfirmedThisSession = false;
606
913
  }
607
- }
608
- if (this._discoveredDevices.size === 0) {
609
914
  await this._ensureDevicePermission();
915
+ const devices = await this.connector.searchDevices();
916
+ this._discoveredDevices.clear();
917
+ for (const d of devices) {
918
+ if (d.connectId) {
919
+ this._discoveredDevices.set(d.connectId, this.connectorDeviceToDeviceInfo(d));
920
+ }
921
+ }
922
+ if (this._discoveredDevices.size === 0) {
923
+ await this._ensureDevicePermission();
924
+ }
925
+ const result = Array.from(this._discoveredDevices.values());
926
+ debugLog("[LedgerAdapter][RES]", {
927
+ method: "searchDevices",
928
+ success: true,
929
+ payload: result
930
+ });
931
+ return result;
932
+ } catch (err) {
933
+ const e = err;
934
+ debugLog("[LedgerAdapter][RES]", {
935
+ method: "searchDevices",
936
+ success: false,
937
+ error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
938
+ });
939
+ throw err;
610
940
  }
611
- debugLog(
612
- `[LedgerAdapter] searchDevices() return count=${this._discoveredDevices.size} ids=[${[
613
- ...this._discoveredDevices.keys()
614
- ].join(",")}]`
615
- );
616
- return Array.from(this._discoveredDevices.values());
617
941
  }
618
942
  // USB single-session invariant: evict all sessions, best-effort (see connectDevice).
619
943
  async _evictAllSessions() {
@@ -629,14 +953,15 @@ var _LedgerAdapter = class _LedgerAdapter {
629
953
  }
630
954
  static _createDeviceBusyError(method) {
631
955
  return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
632
- code: HardwareErrorCode2.DeviceBusy
956
+ code: HardwareErrorCode3.DeviceBusy
633
957
  });
634
958
  }
635
959
  async connectDevice(connectId) {
960
+ debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
636
961
  try {
637
962
  if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
638
963
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
639
- code: HardwareErrorCode2.DeviceNotFound
964
+ code: HardwareErrorCode3.DeviceNotFound
640
965
  });
641
966
  }
642
967
  if (!isLedgerBleConnectionType(this.connector.connectionType)) {
@@ -648,28 +973,79 @@ var _LedgerAdapter = class _LedgerAdapter {
648
973
  if (session.deviceInfo) {
649
974
  this._discoveredDevices.set(connectId, session.deviceInfo);
650
975
  }
651
- return success(connectId);
976
+ const result = success2(connectId);
977
+ debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
978
+ return result;
652
979
  } catch (err) {
653
- return this.errorToFailure(err);
980
+ const failureResult = this.errorToFailure(err);
981
+ debugLog("[LedgerAdapter][RES]", {
982
+ method: "connectDevice",
983
+ success: false,
984
+ payload: failureResult
985
+ });
986
+ return failureResult;
654
987
  }
655
988
  }
656
989
  async disconnectDevice(connectId) {
657
- const sessionId = this._sessions.get(connectId);
658
- if (sessionId) {
659
- await this.connector.disconnect(sessionId);
660
- this._sessions.delete(connectId);
990
+ debugLog("[LedgerAdapter][REQ]", {
991
+ method: "disconnectDevice",
992
+ connectId,
993
+ params: { connectId }
994
+ });
995
+ try {
996
+ const sessionId = this._sessions.get(connectId);
997
+ if (sessionId) {
998
+ await this.connector.disconnect(sessionId);
999
+ this._sessions.delete(connectId);
1000
+ }
1001
+ debugLog("[LedgerAdapter][RES]", { method: "disconnectDevice", success: true });
1002
+ } catch (err) {
1003
+ const e = err;
1004
+ debugLog("[LedgerAdapter][RES]", {
1005
+ method: "disconnectDevice",
1006
+ success: false,
1007
+ error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1008
+ });
1009
+ throw err;
661
1010
  }
662
1011
  }
663
1012
  async getDeviceInfo(connectId, deviceId) {
664
- await this._ensureDevicePermission(connectId, deviceId);
665
- const cached = this._discoveredDevices.get(connectId) ?? Array.from(this._discoveredDevices.values()).find((d) => d.deviceId === deviceId);
666
- if (cached) {
667
- return success(cached);
1013
+ debugLog("[LedgerAdapter][REQ]", {
1014
+ method: "getDeviceInfo",
1015
+ connectId,
1016
+ params: { connectId, deviceId }
1017
+ });
1018
+ try {
1019
+ await this._ensureDevicePermission(connectId, deviceId);
1020
+ const cached = this._discoveredDevices.get(connectId) ?? Array.from(this._discoveredDevices.values()).find((d) => d.deviceId === deviceId);
1021
+ if (cached) {
1022
+ const result = success2(cached);
1023
+ debugLog("[LedgerAdapter][RES]", {
1024
+ method: "getDeviceInfo",
1025
+ success: true,
1026
+ payload: result
1027
+ });
1028
+ return result;
1029
+ }
1030
+ const notFound = failure2(
1031
+ HardwareErrorCode3.DeviceNotFound,
1032
+ "Device not found in cache. Call searchDevices() or wait for a device-connected event first."
1033
+ );
1034
+ debugLog("[LedgerAdapter][RES]", {
1035
+ method: "getDeviceInfo",
1036
+ success: false,
1037
+ payload: notFound
1038
+ });
1039
+ return notFound;
1040
+ } catch (err) {
1041
+ const e = err;
1042
+ debugLog("[LedgerAdapter][RES]", {
1043
+ method: "getDeviceInfo",
1044
+ success: false,
1045
+ error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1046
+ });
1047
+ throw err;
668
1048
  }
669
- return failure(
670
- HardwareErrorCode2.DeviceNotFound,
671
- "Device not found in cache. Call searchDevices() or wait for a device-connected event first."
672
- );
673
1049
  }
674
1050
  getSupportedChains() {
675
1051
  return ["evm", "btc", "sol", "tron"];
@@ -677,7 +1053,7 @@ var _LedgerAdapter = class _LedgerAdapter {
677
1053
  // ---------------------------------------------------------------------------
678
1054
  // Chain call helper
679
1055
  // ---------------------------------------------------------------------------
680
- async callChain(connectId, deviceId, chain, method, params, commonParams, skipFingerprint = false) {
1056
+ async callChain(connectId, deviceId, chain, method, params, commonParams, skipFingerprint = false, installContext) {
681
1057
  try {
682
1058
  const result = await this.connectorCall(
683
1059
  connectId,
@@ -689,9 +1065,10 @@ var _LedgerAdapter = class _LedgerAdapter {
689
1065
  skipFingerprint
690
1066
  },
691
1067
  void 0,
692
- commonParams
1068
+ commonParams,
1069
+ installContext
693
1070
  );
694
- return success(result);
1071
+ return success2(result);
695
1072
  } catch (err) {
696
1073
  return this.errorToFailure(err);
697
1074
  }
@@ -876,7 +1253,7 @@ var _LedgerAdapter = class _LedgerAdapter {
876
1253
  async installApp(connectId, appName) {
877
1254
  try {
878
1255
  await this.connectorCall(connectId, "installApp", { appName });
879
- return success(void 0);
1256
+ return success2(void 0);
880
1257
  } catch (err) {
881
1258
  return this.errorToFailure(err);
882
1259
  }
@@ -884,7 +1261,16 @@ var _LedgerAdapter = class _LedgerAdapter {
884
1261
  async listInstalledApps(connectId) {
885
1262
  try {
886
1263
  const result = await this.connectorCall(connectId, "listInstalledApps", {});
887
- return success(result);
1264
+ return success2(result);
1265
+ } catch (err) {
1266
+ return this.errorToFailure(err);
1267
+ }
1268
+ }
1269
+ // Offline app-presence + unlock probe. No manager-api catalog (unlike listInstalledApps).
1270
+ async listInstalledNames(connectId) {
1271
+ try {
1272
+ const result = await this.connectorCall(connectId, "listInstalledNames", {});
1273
+ return success2(result);
888
1274
  } catch (err) {
889
1275
  return this.errorToFailure(err);
890
1276
  }
@@ -892,7 +1278,7 @@ var _LedgerAdapter = class _LedgerAdapter {
892
1278
  async listAvailableApps(connectId) {
893
1279
  try {
894
1280
  const result = await this.connectorCall(connectId, "listAvailableApps", {});
895
- return success(result);
1281
+ return success2(result);
896
1282
  } catch (err) {
897
1283
  return this.errorToFailure(err);
898
1284
  }
@@ -900,7 +1286,7 @@ var _LedgerAdapter = class _LedgerAdapter {
900
1286
  async getLedgerFirmwareVersion(connectId) {
901
1287
  try {
902
1288
  const result = await this.connectorCall(connectId, "getFirmwareVersion", {});
903
- return success(result);
1289
+ return success2(result);
904
1290
  } catch (err) {
905
1291
  return this.errorToFailure(err);
906
1292
  }
@@ -908,7 +1294,7 @@ var _LedgerAdapter = class _LedgerAdapter {
908
1294
  async getLedgerDeviceInfo(connectId) {
909
1295
  try {
910
1296
  const result = await this.connectorCall(connectId, "getDeviceInfo", {});
911
- return success(result);
1297
+ return success2(result);
912
1298
  } catch (err) {
913
1299
  return this.errorToFailure(err);
914
1300
  }
@@ -921,7 +1307,7 @@ var _LedgerAdapter = class _LedgerAdapter {
921
1307
  }
922
1308
  cancel(connectId) {
923
1309
  const userAbortReason = Object.assign(new Error("User aborted operation"), {
924
- code: HardwareErrorCode2.UserAborted,
1310
+ code: HardwareErrorCode3.UserAborted,
925
1311
  _tag: ERROR_TAG.UserAborted
926
1312
  });
927
1313
  this._lastCancelReason = userAbortReason;
@@ -955,7 +1341,7 @@ var _LedgerAdapter = class _LedgerAdapter {
955
1341
  chain,
956
1342
  (method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
957
1343
  );
958
- return success(fingerprint);
1344
+ return success2(fingerprint);
959
1345
  } catch (err) {
960
1346
  debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
961
1347
  return this.errorToFailure(err);
@@ -978,7 +1364,7 @@ var _LedgerAdapter = class _LedgerAdapter {
978
1364
  return { success: false, expected: deviceId, actual: fingerprint };
979
1365
  } catch (err) {
980
1366
  const mapped = mapLedgerError(err);
981
- if (mapped.code === HardwareErrorCode2.WrongApp || mapped.code === HardwareErrorCode2.DeviceLocked) {
1367
+ if (mapped.code === HardwareErrorCode3.WrongApp || mapped.code === HardwareErrorCode3.DeviceLocked) {
982
1368
  return { success: true };
983
1369
  }
984
1370
  throw err;
@@ -1058,14 +1444,14 @@ var _LedgerAdapter = class _LedgerAdapter {
1058
1444
  });
1059
1445
  throw Object.assign(new Error("User cancelled Ledger connection"), {
1060
1446
  _tag: ERROR_TAG.UserAborted,
1061
- code: HardwareErrorCode2.UserAborted,
1447
+ code: HardwareErrorCode3.UserAborted,
1062
1448
  cause: err
1063
1449
  });
1064
1450
  }
1065
1451
  if (!payload?.confirmed) {
1066
1452
  throw Object.assign(new Error("User cancelled Ledger connection"), {
1067
1453
  _tag: ERROR_TAG.UserAborted,
1068
- code: HardwareErrorCode2.UserAborted
1454
+ code: HardwareErrorCode3.UserAborted
1069
1455
  });
1070
1456
  }
1071
1457
  await new Promise((resolve) => {
@@ -1144,7 +1530,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1144
1530
  if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
1145
1531
  if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
1146
1532
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
1147
- code: HardwareErrorCode2.DeviceNotFound
1533
+ code: HardwareErrorCode3.DeviceNotFound
1148
1534
  });
1149
1535
  }
1150
1536
  if (connectId && this._sessions.has(connectId)) return connectId;
@@ -1154,7 +1540,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1154
1540
  new Error(
1155
1541
  "Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
1156
1542
  ),
1157
- { code: HardwareErrorCode2.DeviceOneDeviceOnly }
1543
+ { code: HardwareErrorCode3.DeviceOneDeviceOnly }
1158
1544
  );
1159
1545
  }
1160
1546
  return this._sessions.keys().next().value;
@@ -1234,7 +1620,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1234
1620
  new Error(
1235
1621
  "Device not connected after multiple attempts. Please ensure your Ledger is awake, unlocked, and in range, then try again."
1236
1622
  ),
1237
- { code: HardwareErrorCode2.DeviceNotFound }
1623
+ { code: HardwareErrorCode3.DeviceNotFound }
1238
1624
  );
1239
1625
  }
1240
1626
  await this._waitForDeviceConnect(internalSignal);
@@ -1258,7 +1644,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1258
1644
  return this._connectDeviceOrThrow(devices[0].connectId);
1259
1645
  }
1260
1646
  const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
1261
- code: HardwareErrorCode2.DeviceNotFound
1647
+ code: HardwareErrorCode3.DeviceNotFound
1262
1648
  });
1263
1649
  if (isLedgerBleConnectionType(this.connector.connectionType)) {
1264
1650
  err._tag = ERROR_TAG.DeviceNotAdvertising;
@@ -1267,7 +1653,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1267
1653
  }
1268
1654
  if (isLedgerBleConnectionType(this.connector.connectionType)) {
1269
1655
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
1270
- code: HardwareErrorCode2.DeviceNotFound
1656
+ code: HardwareErrorCode3.DeviceNotFound
1271
1657
  });
1272
1658
  }
1273
1659
  if (devices.length > 1) {
@@ -1275,7 +1661,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1275
1661
  }
1276
1662
  if (devices.length !== 1) {
1277
1663
  throw Object.assign(new Error("Ledger device not found."), {
1278
- code: HardwareErrorCode2.DeviceNotFound
1664
+ code: HardwareErrorCode3.DeviceNotFound
1279
1665
  });
1280
1666
  }
1281
1667
  return this._connectDeviceOrThrow(devices[0].connectId);
@@ -1326,26 +1712,43 @@ var _LedgerAdapter = class _LedgerAdapter {
1326
1712
  const result = signal ? await this._abortable(signal, promise) : await promise;
1327
1713
  return this._unwrapConnectorResult(result);
1328
1714
  }
1329
- async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams) {
1330
- debugLog("[LedgerAdapter] connectorCall:", method, "connectId:", connectId || "(empty)");
1715
+ async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
1716
+ debugLog("[LedgerAdapter][REQ]", { method, connectId: connectId || "(empty)", params });
1331
1717
  const queueKey = connectId || "__ledger_default__";
1332
- return this._jobQueue.enqueue(
1333
- queueKey,
1334
- async (signal) => this._runConnectorCall(
1335
- connectId,
1718
+ try {
1719
+ const result = await this._jobQueue.enqueue(
1720
+ queueKey,
1721
+ async (signal) => this._runConnectorCall(
1722
+ connectId,
1723
+ method,
1724
+ params,
1725
+ signal,
1726
+ fingerprint,
1727
+ permissionDeviceId,
1728
+ commonParams,
1729
+ installContext
1730
+ ),
1731
+ {
1732
+ label: method,
1733
+ rejectIfBusy: true,
1734
+ busyError: _LedgerAdapter._createDeviceBusyError(method)
1735
+ }
1736
+ );
1737
+ debugLog("[LedgerAdapter][RES]", { method, success: true, payload: result });
1738
+ return result;
1739
+ } catch (err) {
1740
+ const e = err;
1741
+ debugLog("[LedgerAdapter][RES]", {
1336
1742
  method,
1337
- params,
1338
- signal,
1339
- fingerprint,
1340
- permissionDeviceId,
1341
- commonParams
1342
- ),
1343
- {
1344
- label: method,
1345
- rejectIfBusy: true,
1346
- busyError: _LedgerAdapter._createDeviceBusyError(method)
1347
- }
1348
- );
1743
+ success: false,
1744
+ error: {
1745
+ message: e?.message,
1746
+ _tag: e?._tag,
1747
+ code: e?.code ?? e?.errorCode
1748
+ }
1749
+ });
1750
+ throw err;
1751
+ }
1349
1752
  }
1350
1753
  /**
1351
1754
  * Race a promise against an abort signal. On abort, rejects with
@@ -1380,7 +1783,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1380
1783
  }
1381
1784
  }
1382
1785
  /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
1383
- async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams) {
1786
+ async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
1384
1787
  _LedgerAdapter._throwIfAborted(signal);
1385
1788
  await this._ensureDevicePermission(
1386
1789
  connectId,
@@ -1394,7 +1797,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1394
1797
  if (gatedParams === null) {
1395
1798
  throw Object.assign(new Error("User cancelled BTC high-index confirmation"), {
1396
1799
  _tag: ERROR_TAG.UserAborted,
1397
- code: HardwareErrorCode2.UserAborted
1800
+ code: HardwareErrorCode3.UserAborted
1398
1801
  });
1399
1802
  }
1400
1803
  effectiveParams = gatedParams;
@@ -1423,7 +1826,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1423
1826
  );
1424
1827
  if (!fp.success) {
1425
1828
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1426
- code: HardwareErrorCode2.DeviceMismatch
1829
+ code: HardwareErrorCode3.DeviceMismatch
1427
1830
  });
1428
1831
  }
1429
1832
  }
@@ -1442,22 +1845,62 @@ var _LedgerAdapter = class _LedgerAdapter {
1442
1845
  isStuckApp: isStuckAppStateError(err)
1443
1846
  });
1444
1847
  const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
1445
- const isAppMissing = isAppNotInstalledError(err) || err?.code === HardwareErrorCode2.AppNotInstalled;
1848
+ const isAppMissing = isAppNotInstalledError(err) || err?.code === HardwareErrorCode3.AppNotInstalled;
1446
1849
  if (autoInstallApp && isAppMissing) {
1850
+ if (installContext?.deviceOutOfMemoryError) {
1851
+ throw installContext.deviceOutOfMemoryError;
1852
+ }
1447
1853
  const appName = err?.appName ?? mapLedgerError(err).appName;
1448
1854
  if (appName) {
1855
+ if (installContext?.installAttemptedAppNames?.has(appName)) {
1856
+ throw createHwkError({
1857
+ code: HardwareErrorCode3.AppNotInstalled,
1858
+ message: `${appName} install reported success but the app is still missing on device`,
1859
+ _tag: ERROR_TAG.AppInstallVerifyFailed,
1860
+ appName
1861
+ });
1862
+ }
1449
1863
  const confirmed = await this._waitForInstallAppConfirm(appName);
1450
- if (!confirmed) throw err;
1864
+ if (!confirmed) {
1865
+ throw createHwkError({
1866
+ code: HardwareErrorCode3.UserAborted,
1867
+ message: `User declined to install ${appName}`,
1868
+ _tag: ERROR_TAG.UserAborted,
1869
+ appName
1870
+ });
1871
+ }
1451
1872
  this.emitter.emit("ui-event", {
1452
1873
  type: EConnectorInteraction.AppInstallProgress,
1453
1874
  payload: { connectId: resolvedConnectId, appName, progress: 0 }
1454
1875
  });
1455
- await this._callConnector(sessionId, "installApp", { appName }, signal);
1876
+ try {
1877
+ await this._callConnector(sessionId, "installApp", { appName }, signal);
1878
+ } catch (installErr) {
1879
+ if (mapLedgerError(installErr).code === HardwareErrorCode3.DeviceOutOfMemory) {
1880
+ if (installContext) {
1881
+ installContext.deviceOutOfMemoryError = installErr;
1882
+ }
1883
+ }
1884
+ throw installErr;
1885
+ }
1886
+ if (installContext) {
1887
+ installContext.installAttemptedAppNames = installContext.installAttemptedAppNames ?? /* @__PURE__ */ new Set();
1888
+ installContext.installAttemptedAppNames.add(appName);
1889
+ }
1456
1890
  this.emitter.emit(UI_REQUEST.CLOSE_UI_WINDOW, {
1457
1891
  type: UI_REQUEST.CLOSE_UI_WINDOW,
1458
1892
  payload: {}
1459
1893
  });
1460
- return await this._callConnector(sessionId, method, effectiveParams, signal);
1894
+ return await this._runConnectorCall(
1895
+ resolvedConnectId,
1896
+ method,
1897
+ effectiveParams,
1898
+ signal,
1899
+ fingerprint,
1900
+ permissionDeviceId,
1901
+ commonParams,
1902
+ installContext
1903
+ );
1461
1904
  }
1462
1905
  }
1463
1906
  if (isStuckAppStateError(err)) {
@@ -1532,7 +1975,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1532
1975
  );
1533
1976
  if (!fp.success) {
1534
1977
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1535
- code: HardwareErrorCode2.DeviceMismatch
1978
+ code: HardwareErrorCode3.DeviceMismatch
1536
1979
  });
1537
1980
  }
1538
1981
  }
@@ -1567,7 +2010,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1567
2010
  this.connector.reset();
1568
2011
  const codeNum = err?.code;
1569
2012
  throw Object.assign(err, {
1570
- code: codeNum ?? HardwareErrorCode2.DeviceDisconnected
2013
+ code: codeNum ?? HardwareErrorCode3.DeviceDisconnected
1571
2014
  });
1572
2015
  }
1573
2016
  throw err;
@@ -1601,7 +2044,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1601
2044
  );
1602
2045
  if (!fp.success) {
1603
2046
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1604
- code: HardwareErrorCode2.DeviceMismatch
2047
+ code: HardwareErrorCode3.DeviceMismatch
1605
2048
  });
1606
2049
  }
1607
2050
  }
@@ -1658,7 +2101,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1658
2101
  );
1659
2102
  if (!fp.success) {
1660
2103
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1661
- code: HardwareErrorCode2.DeviceMismatch
2104
+ code: HardwareErrorCode3.DeviceMismatch
1662
2105
  });
1663
2106
  }
1664
2107
  }
@@ -1699,7 +2142,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1699
2142
  );
1700
2143
  if (!fp.success) {
1701
2144
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1702
- code: HardwareErrorCode2.DeviceMismatch
2145
+ code: HardwareErrorCode3.DeviceMismatch
1703
2146
  });
1704
2147
  }
1705
2148
  }
@@ -1749,7 +2192,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1749
2192
  });
1750
2193
  throw Object.assign(new Error("Device permission request superseded"), {
1751
2194
  _tag: ERROR_TAG.UserAborted,
1752
- code: HardwareErrorCode2.UserAborted,
2195
+ code: HardwareErrorCode3.UserAborted,
1753
2196
  cause: err
1754
2197
  });
1755
2198
  }
@@ -1760,7 +2203,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1760
2203
  const { granted, reason, message } = response;
1761
2204
  if (!granted) {
1762
2205
  throw Object.assign(new Error(message ?? "Device permission denied"), {
1763
- code: HardwareErrorCode2.DevicePermissionDenied,
2206
+ code: HardwareErrorCode3.DevicePermissionDenied,
1764
2207
  reason
1765
2208
  });
1766
2209
  }
@@ -1774,7 +2217,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1774
2217
  const tag = err && typeof err === "object" ? err._tag : void 0;
1775
2218
  if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
1776
2219
  const e = err;
1777
- const params = e.code === HardwareErrorCode2.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
2220
+ const params = e.code === HardwareErrorCode3.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
1778
2221
  return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
1779
2222
  }
1780
2223
  const mapped = mapLedgerError(err);
@@ -1821,12 +2264,15 @@ _LedgerAdapter.STUCK_APP_RETRY_DELAY_MS = 500;
1821
2264
  // Confirm but device never shows up / never unlocks), throw DeviceNotFound
1822
2265
  // instead of looping forever.
1823
2266
  _LedgerAdapter.MAX_DOCONNECT_CONFIRMS = 3;
2267
+ // Throttle AppInstallProgress by progress delta — DMK streams much faster
2268
+ // than UIs need. Final frame (progress >= 1) always passes.
2269
+ _LedgerAdapter.APP_INSTALL_PROGRESS_MIN_DELTA = 0.05;
1824
2270
  var LedgerAdapter = _LedgerAdapter;
1825
2271
 
1826
2272
  // src/connector/LedgerConnectorBase.ts
1827
2273
  import {
1828
2274
  EConnectorInteraction as EConnectorInteraction4,
1829
- HardwareErrorCode as HardwareErrorCode7,
2275
+ HardwareErrorCode as HardwareErrorCode8,
1830
2276
  serializeConnectorError
1831
2277
  } from "@onekeyfe/hwk-adapter-core";
1832
2278
 
@@ -2383,7 +2829,7 @@ var SignerManager = class _SignerManager {
2383
2829
  };
2384
2830
 
2385
2831
  // src/connector/chains/evm.ts
2386
- import { HardwareErrorCode as HardwareErrorCode3, padHex64, stripHex } from "@onekeyfe/hwk-adapter-core";
2832
+ import { HardwareErrorCode as HardwareErrorCode4, padHex64, stripHex } from "@onekeyfe/hwk-adapter-core";
2387
2833
 
2388
2834
  // src/connector/chains/utils.ts
2389
2835
  import { EConnectorInteraction as EConnectorInteraction2 } from "@onekeyfe/hwk-adapter-core";
@@ -2425,7 +2871,7 @@ async function evmSignTransaction(ctx, sessionId, params) {
2425
2871
  new Error(
2426
2872
  "Ledger requires a pre-serialized transaction (serializedTx). Provide an RLP-encoded hex string."
2427
2873
  ),
2428
- { code: HardwareErrorCode3.InvalidParams }
2874
+ { code: HardwareErrorCode4.InvalidParams }
2429
2875
  );
2430
2876
  }
2431
2877
  const signer = await _getEthSigner(ctx, sessionId);
@@ -2466,7 +2912,7 @@ async function evmSignTypedData(ctx, sessionId, params) {
2466
2912
  new Error(
2467
2913
  'Ledger does not support hash-only EIP-712 signing. Use mode "full" with the complete typed data structure.'
2468
2914
  ),
2469
- { code: HardwareErrorCode3.MethodNotSupported }
2915
+ { code: HardwareErrorCode4.MethodNotSupported }
2470
2916
  );
2471
2917
  }
2472
2918
  const signer = await _getEthSigner(ctx, sessionId);
@@ -2501,7 +2947,7 @@ async function _getEthSigner(ctx, sessionId) {
2501
2947
  }
2502
2948
 
2503
2949
  // src/connector/chains/btc.ts
2504
- import { HardwareErrorCode as HardwareErrorCode4, stripHex as stripHex2 } from "@onekeyfe/hwk-adapter-core";
2950
+ import { HardwareErrorCode as HardwareErrorCode5, stripHex as stripHex2 } from "@onekeyfe/hwk-adapter-core";
2505
2951
  import { Psbt } from "bitcoinjs-lib";
2506
2952
 
2507
2953
  // src/signer/SignerBtc.ts
@@ -2622,7 +3068,7 @@ function _purposeToTemplate(purpose, DDT) {
2622
3068
  return DDT.TAPROOT;
2623
3069
  default:
2624
3070
  throw Object.assign(new Error(`Unsupported BTC purpose: m/${purpose ?? "<undefined>"}'`), {
2625
- code: HardwareErrorCode4.InvalidParams
3071
+ code: HardwareErrorCode5.InvalidParams
2626
3072
  });
2627
3073
  }
2628
3074
  }
@@ -2678,7 +3124,7 @@ async function btcSignTransaction(ctx, sessionId, params) {
2678
3124
  if (!params.psbt) {
2679
3125
  throw Object.assign(
2680
3126
  new Error("Ledger requires PSBT format for BTC transaction signing. Provide params.psbt."),
2681
- { code: HardwareErrorCode4.InvalidParams }
3127
+ { code: HardwareErrorCode5.InvalidParams }
2682
3128
  );
2683
3129
  }
2684
3130
  const btcSigner = await _createBtcSigner(ctx, sessionId);
@@ -2703,7 +3149,7 @@ async function btcSignTransaction(ctx, sessionId, params) {
2703
3149
  async function btcSignPsbt(ctx, sessionId, params) {
2704
3150
  if (!params.psbt) {
2705
3151
  throw Object.assign(new Error("btcSignPsbt requires params.psbt"), {
2706
- code: HardwareErrorCode4.InvalidParams
3152
+ code: HardwareErrorCode5.InvalidParams
2707
3153
  });
2708
3154
  }
2709
3155
  const btcSigner = await _createBtcSigner(ctx, sessionId);
@@ -2948,7 +3394,7 @@ async function _createSolSigner(ctx, sessionId) {
2948
3394
  }
2949
3395
 
2950
3396
  // src/connector/chains/tron.ts
2951
- import { HardwareErrorCode as HardwareErrorCode6 } from "@onekeyfe/hwk-adapter-core";
3397
+ import { HardwareErrorCode as HardwareErrorCode7 } from "@onekeyfe/hwk-adapter-core";
2952
3398
  import Trx from "@ledgerhq/hw-app-trx";
2953
3399
 
2954
3400
  // src/connector/chains/legacyChainCall.ts
@@ -2961,7 +3407,7 @@ import {
2961
3407
  OpenAppCommand,
2962
3408
  isSuccessCommandResult
2963
3409
  } from "@ledgerhq/device-management-kit";
2964
- import { HardwareErrorCode as HardwareErrorCode5 } from "@onekeyfe/hwk-adapter-core";
3410
+ import { HardwareErrorCode as HardwareErrorCode6 } from "@onekeyfe/hwk-adapter-core";
2965
3411
  var APP_NAME_MAP = {
2966
3412
  ETH: "Ethereum",
2967
3413
  BTC: "Bitcoin",
@@ -3065,15 +3511,30 @@ var AppManager = class {
3065
3511
  command: new OpenAppCommand({ appName })
3066
3512
  });
3067
3513
  if (!isSuccessCommandResult(result)) {
3068
- const { statusCode } = result;
3069
- const hasStatusCode2 = statusCode != null && statusCode !== "";
3070
- debugLog("[AppManager] openApp failed:", appName, "statusCode:", statusCode);
3514
+ const dmkErr = result.error;
3515
+ const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
3516
+ const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
3517
+ debugLog(
3518
+ "[AppManager] openApp failed:",
3519
+ appName,
3520
+ "errorCode:",
3521
+ errorCode,
3522
+ "tag:",
3523
+ dmkErr._tag
3524
+ );
3525
+ let code;
3526
+ if (errorCode === "6807" || /unknown application/i.test(message)) {
3527
+ code = HardwareErrorCode6.AppNotInstalled;
3528
+ } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
3529
+ code = HardwareErrorCode6.UserRejected;
3530
+ }
3071
3531
  throw Object.assign(new Error(`Failed to open "${appName}"`), {
3072
3532
  _tag: ERROR_TAG.OpenAppCommand,
3073
- code: hasStatusCode2 ? void 0 : HardwareErrorCode5.AppNotInstalled,
3074
- errorCode: String(statusCode ?? ""),
3075
- statusCode,
3076
- appName
3533
+ code,
3534
+ errorCode,
3535
+ statusCode: errorCode,
3536
+ appName,
3537
+ originalError: dmkErr
3077
3538
  });
3078
3539
  }
3079
3540
  }
@@ -3252,7 +3713,7 @@ async function tronSignTransaction(ctx, sessionId, params) {
3252
3713
  if (!params.rawTxHex) {
3253
3714
  throw Object.assign(
3254
3715
  new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
3255
- { code: HardwareErrorCode6.InvalidParams }
3716
+ { code: HardwareErrorCode7.InvalidParams }
3256
3717
  );
3257
3718
  }
3258
3719
  const path = normalizePath(params.path);
@@ -3413,6 +3874,22 @@ var DeviceApps = class {
3413
3874
  );
3414
3875
  return result.filter((a) => a !== null).map(applicationToMetadata);
3415
3876
  }
3877
+ /** Installed app names via ListApps APDU. Offline (no manager-api), but still requires unlock + dashboard. */
3878
+ async listInstalledNames(options) {
3879
+ const action = this._dmk.executeDeviceAction({
3880
+ sessionId: this._sessionId,
3881
+ deviceAction: new this._ledgerKit.ListAppsDeviceAction({
3882
+ input: { unlockTimeout: options?.unlockTimeout }
3883
+ })
3884
+ });
3885
+ const result = await deviceActionToPromise(
3886
+ action,
3887
+ this.onInteraction,
3888
+ void 0,
3889
+ this.onRegisterCanceller
3890
+ );
3891
+ return result.map((entry) => entry?.appName).filter((name) => typeof name === "string" && name.length > 0);
3892
+ }
3416
3893
  // Catalog lookup via custom device action — DMK has no typed wrapper for this.
3417
3894
  async listAvailable() {
3418
3895
  const customAction = new ListAvailableAppsDeviceAction({
@@ -3476,6 +3953,22 @@ var DeviceApps = class {
3476
3953
  async install(appName, onProgress, options) {
3477
3954
  if (!appName) throw new Error("DeviceApps.install: appName is required");
3478
3955
  debugLog("[DeviceApps] install:", appName);
3956
+ const refreshAction = this._dmk.executeDeviceAction({
3957
+ sessionId: this._sessionId,
3958
+ deviceAction: new this._ledgerKit.GetDeviceMetadataDeviceAction({
3959
+ input: {
3960
+ useSecureChannel: true,
3961
+ forceUpdate: true,
3962
+ unlockTimeout: options?.unlockTimeout
3963
+ }
3964
+ })
3965
+ });
3966
+ await deviceActionToPromise(
3967
+ refreshAction,
3968
+ this.onInteraction,
3969
+ INSTALL_TIMEOUT_MS,
3970
+ this.onRegisterCanceller
3971
+ );
3479
3972
  const action = this._dmk.executeDeviceAction({
3480
3973
  sessionId: this._sessionId,
3481
3974
  deviceAction: new this._ledgerKit.InstallOrUpdateAppsDeviceAction({
@@ -3553,7 +4046,7 @@ var METHOD_PREFIX_TO_APP_NAME = {
3553
4046
  tron: "Tron"
3554
4047
  };
3555
4048
  var HARDWARE_ERROR_CODE_VALUES = new Set(
3556
- Object.values(HardwareErrorCode7).filter((value) => typeof value === "number")
4049
+ Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
3557
4050
  );
3558
4051
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
3559
4052
  async function defaultLedgerKitImporter(pkg) {
@@ -3724,7 +4217,7 @@ var LedgerConnectorBase = class {
3724
4217
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
3725
4218
  );
3726
4219
  err._tag = ERROR_TAG.BlePairingTimeout;
3727
- err.code = HardwareErrorCode7.BlePairingTimeout;
4220
+ err.code = HardwareErrorCode8.BlePairingTimeout;
3728
4221
  reject(err);
3729
4222
  }, HANG_CEILING_MS);
3730
4223
  });
@@ -3757,7 +4250,7 @@ var LedgerConnectorBase = class {
3757
4250
  "Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
3758
4251
  );
3759
4252
  err._tag = ERROR_TAG.DeviceNotAdvertising;
3760
- err.code = HardwareErrorCode7.DeviceNotFound;
4253
+ err.code = HardwareErrorCode8.DeviceNotFound;
3761
4254
  throw err;
3762
4255
  };
3763
4256
  const doConnect = async (path) => {
@@ -3821,7 +4314,7 @@ var LedgerConnectorBase = class {
3821
4314
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
3822
4315
  );
3823
4316
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
3824
- wrapped.code = HardwareErrorCode7.BlePairingTimeout;
4317
+ wrapped.code = HardwareErrorCode8.BlePairingTimeout;
3825
4318
  wrapped.originalError = err;
3826
4319
  throw wrapped;
3827
4320
  }
@@ -3899,7 +4392,7 @@ var LedgerConnectorBase = class {
3899
4392
  this._unwatchSessionState(sessionId);
3900
4393
  this._signerManager?.invalidate(sessionId);
3901
4394
  this._cancellers.get(sessionId)?.({
3902
- code: HardwareErrorCode7.DeviceDisconnected,
4395
+ code: HardwareErrorCode8.DeviceDisconnected,
3903
4396
  tag: "DeviceDisconnected",
3904
4397
  message: "Device disconnected"
3905
4398
  });
@@ -3925,7 +4418,7 @@ var LedgerConnectorBase = class {
3925
4418
  success: false,
3926
4419
  error: serializeConnectorError(
3927
4420
  Object.assign(new Error("Ledger app is unresponsive"), {
3928
- code: HardwareErrorCode7.DeviceAppStuck,
4421
+ code: HardwareErrorCode8.DeviceAppStuck,
3929
4422
  _tag: ERROR_TAG.DeviceAppStuck,
3930
4423
  originalError: err
3931
4424
  })
@@ -3937,7 +4430,7 @@ var LedgerConnectorBase = class {
3937
4430
  success: false,
3938
4431
  error: serializeConnectorError(
3939
4432
  Object.assign(new Error("Device communication interrupted, please retry"), {
3940
- code: HardwareErrorCode7.TransportError,
4433
+ code: HardwareErrorCode8.TransportError,
3941
4434
  _tag: ERROR_TAG.DeviceTransportStuck,
3942
4435
  originalError: err
3943
4436
  })
@@ -4033,6 +4526,17 @@ var LedgerConnectorBase = class {
4033
4526
  ctx.clearCanceller(sessionId);
4034
4527
  }
4035
4528
  }
4529
+ case "listInstalledNames": {
4530
+ const apps = await _getDeviceApps(ctx, sessionId);
4531
+ try {
4532
+ return await apps.listInstalledNames(params);
4533
+ } catch (err) {
4534
+ ctx.invalidateSession(sessionId);
4535
+ throw ctx.wrapError(err);
4536
+ } finally {
4537
+ ctx.clearCanceller(sessionId);
4538
+ }
4539
+ }
4036
4540
  case "listAvailableApps": {
4037
4541
  const apps = await _getDeviceApps(ctx, sessionId);
4038
4542
  try {