@onekeyfe/hwk-keystone-connector-usb 1.2.3-alpha.2 → 1.2.3-alpha.4

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.
@@ -1,13 +1,44 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/errors.ts
2
2
  var _hwkadaptercore = require('@onekeyfe/hwk-adapter-core');
3
3
  var _hwtransporterror = require('@keystonehq/hw-transport-error');
4
+ function isDeviceStatus(statusCode) {
5
+ return statusCode >= 0 && statusCode < _hwtransporterror.Status.ERR_DEVICE_NOT_OPENED;
6
+ }
4
7
  var DEVICE_STATUS_MAP = {
8
+ [_hwtransporterror.Status.RSP_FAILURE_CODE]: _hwkadaptercore.HardwareErrorCode.UnknownError,
9
+ [_hwtransporterror.Status.PRS_INVALID_TOTAL_PACKETS]: _hwkadaptercore.HardwareErrorCode.InvalidParams,
10
+ [_hwtransporterror.Status.PRS_INVALID_INDEX]: _hwkadaptercore.HardwareErrorCode.InvalidParams,
5
11
  [_hwtransporterror.Status.PRS_PARSING_REJECTED]: _hwkadaptercore.HardwareErrorCode.UserRejected,
12
+ [_hwtransporterror.Status.PRS_PARSING_ERROR]: _hwkadaptercore.HardwareErrorCode.InvalidParams,
6
13
  [_hwtransporterror.Status.PRS_PARSING_DISALLOWED]: _hwkadaptercore.HardwareErrorCode.DeviceLocked,
14
+ [_hwtransporterror.Status.PRS_PARSING_UNMATCHED]: _hwkadaptercore.HardwareErrorCode.InvalidParams,
7
15
  [_hwtransporterror.Status.PRS_PARSING_MISMATCHED_WALLET]: _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
16
+ [_hwtransporterror.Status.PRS_PARSING_VERIFY_PASSWORD_ERROR]: _hwkadaptercore.HardwareErrorCode.PinInvalid,
17
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_UNSUPPORTED_CHAIN]: _hwkadaptercore.HardwareErrorCode.ChainNotSupported,
18
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_INVALID_PARAMS]: _hwkadaptercore.HardwareErrorCode.InvalidParams,
19
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_ERROR]: _hwkadaptercore.HardwareErrorCode.UnknownError,
20
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_DISALLOWED]: _hwkadaptercore.HardwareErrorCode.DeviceLocked,
8
21
  [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_REJECTED]: _hwkadaptercore.HardwareErrorCode.UserRejected,
9
- [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_DISALLOWED]: _hwkadaptercore.HardwareErrorCode.DeviceLocked
22
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_BUSY]: _hwkadaptercore.HardwareErrorCode.DeviceBusyInternal
23
+ };
24
+ var DEVICE_STATUS_MESSAGE = {
25
+ [_hwtransporterror.Status.RSP_FAILURE_CODE]: "Keystone reported a failure",
26
+ [_hwtransporterror.Status.PRS_INVALID_TOTAL_PACKETS]: "Keystone rejected the request framing (total packets)",
27
+ [_hwtransporterror.Status.PRS_INVALID_INDEX]: "Keystone rejected the request framing (packet index)",
28
+ [_hwtransporterror.Status.PRS_PARSING_REJECTED]: "Rejected on the Keystone screen",
29
+ [_hwtransporterror.Status.PRS_PARSING_ERROR]: "Keystone could not parse the request",
30
+ [_hwtransporterror.Status.PRS_PARSING_DISALLOWED]: "Keystone declined the request in its current state",
31
+ [_hwtransporterror.Status.PRS_PARSING_UNMATCHED]: "Keystone found no handler for the request",
32
+ [_hwtransporterror.Status.PRS_PARSING_MISMATCHED_WALLET]: "The request belongs to a different Keystone wallet",
33
+ [_hwtransporterror.Status.PRS_PARSING_VERIFY_PASSWORD_ERROR]: "Keystone password verification failed",
34
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_UNSUPPORTED_CHAIN]: "Keystone does not support this chain",
35
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_INVALID_PARAMS]: "Keystone rejected the address export parameters",
36
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_ERROR]: "Keystone failed to export the address",
37
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_DISALLOWED]: "Keystone declined to export the address",
38
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_REJECTED]: "Address export rejected on the Keystone screen",
39
+ [_hwtransporterror.Status.PRS_EXPORT_ADDRESS_BUSY]: "Keystone is busy with another request"
10
40
  };
41
+ var PLACEHOLDER_MESSAGE = /^unknown error\b/i;
11
42
  var CLIENT_STATUS_MAP = {
12
43
  [_hwtransporterror.Status.ERR_DEVICE_NOT_OPENED]: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
13
44
  [_hwtransporterror.Status.ERR_DEVICE_NOT_FOUND]: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
@@ -23,19 +54,21 @@ function mapKeystoneUsbError(err) {
23
54
  const statusCode = err && typeof err === "object" && typeof err.transportErrorCode === "number" ? err.transportErrorCode : void 0;
24
55
  const message = err instanceof Error ? err.message : String(err);
25
56
  if (statusCode !== void 0) {
26
- const deviceCode = DEVICE_STATUS_MAP[statusCode];
27
- const code = _nullishCoalesce(_nullishCoalesce(deviceCode, () => ( CLIENT_STATUS_MAP[statusCode])), () => ( _hwkadaptercore.HardwareErrorCode.TransportError));
57
+ const fromDevice = isDeviceStatus(statusCode);
58
+ const code = fromDevice ? _nullishCoalesce(DEVICE_STATUS_MAP[statusCode], () => ( _hwkadaptercore.HardwareErrorCode.UnknownError)) : _nullishCoalesce(CLIENT_STATUS_MAP[statusCode], () => ( _hwkadaptercore.HardwareErrorCode.TransportError));
28
59
  let origin;
29
- if (deviceCode !== void 0) {
60
+ if (fromDevice) {
30
61
  origin = "device";
31
62
  } else if (code !== _hwkadaptercore.HardwareErrorCode.OperationTimeout) {
32
63
  origin = "transport";
33
64
  }
65
+ const description = DEVICE_STATUS_MESSAGE[statusCode];
34
66
  return _hwkadaptercore.createHwkError.call(void 0, {
35
67
  code,
36
- message,
37
- // A DEVICE_STATUS_MAP hit is the firmware answering (rejection, locked,
38
- // mismatched wallet) a result, never a link problem. Client-side
68
+ // The device's own payload text wins; the table only covers the case
69
+ // where the transport had nothing to forward.
70
+ message: description && PLACEHOLDER_MESSAGE.test(message) ? `${description} (error_code: ${statusCode})` : message,
71
+ // A firmware status word is a result, never a link problem. Client-side
39
72
  // status codes came from the framing/pipe layer — EXCEPT the timeout,
40
73
  // which is genuinely two-faced (the device may be sitting on a confirm
41
74
  // screen waiting for a human, or the pipe may be dead) and stays
@@ -88,9 +121,7 @@ var _hwtransportusb = require('@keystonehq/hw-transport-usb');
88
121
 
89
122
 
90
123
  function usbDisplayName(device) {
91
- const base = _optionalChain([device, 'access', _ => _.productName, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]) || "Keystone";
92
- const suffix = _optionalChain([device, 'access', _4 => _4.serialNumber, 'optionalAccess', _5 => _5.split, 'call', _6 => _6("-"), 'access', _7 => _7.pop, 'call', _8 => _8(), 'optionalAccess', _9 => _9.trim, 'call', _10 => _10()]);
93
- return suffix ? `${base} ${suffix}` : base;
124
+ return _optionalChain([device, 'access', _ => _.productName, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]) || "Keystone";
94
125
  }
95
126
  var DEFAULT_TIMEOUT_MS = 1e5;
96
127
  var KEYSTONE_PUBLIC_DATA_UR_TYPE = "qr-hardware-call";
@@ -130,7 +161,7 @@ var KeystoneUsbConnectorBase = class {
130
161
  /** Wallets that already approved a public-data export, keyed by master fingerprint. */
131
162
  this.publicDataConfirmedWallets = /* @__PURE__ */ new Set();
132
163
  this.transportClass = transportClass;
133
- this.timeoutMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _11 => _11.timeoutMs]), () => ( DEFAULT_TIMEOUT_MS));
164
+ this.timeoutMs = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _4 => _4.timeoutMs]), () => ( DEFAULT_TIMEOUT_MS));
134
165
  }
135
166
  _invalidateDiscoverySnapshot() {
136
167
  this.activeDiscoveryGeneration = void 0;
@@ -141,7 +172,7 @@ var KeystoneUsbConnectorBase = class {
141
172
  this.availabilityDevices.clear();
142
173
  }
143
174
  async searchDevices(options) {
144
- const isAvailabilitySearch = _optionalChain([options, 'optionalAccess', _12 => _12.purpose]) === "availability";
175
+ const isAvailabilitySearch = _optionalChain([options, 'optionalAccess', _5 => _5.purpose]) === "availability";
145
176
  const discoveryGeneration = /* @__PURE__ */ Symbol(
146
177
  isAvailabilitySearch ? "keystone-usb-availability" : "keystone-usb-discovery"
147
178
  );
@@ -152,7 +183,12 @@ var KeystoneUsbConnectorBase = class {
152
183
  this._invalidateDiscoverySnapshot();
153
184
  this.activeDiscoveryGeneration = discoveryGeneration;
154
185
  }
155
- const devices = await this.transportClass.getKeystoneDevices();
186
+ let devices;
187
+ try {
188
+ devices = await this.transportClass.getKeystoneDevices();
189
+ } catch (err) {
190
+ throw mapKeystoneUsbError(err);
191
+ }
156
192
  if ((isAvailabilitySearch ? this.activeAvailabilityGeneration : this.activeDiscoveryGeneration) !== discoveryGeneration) {
157
193
  return [];
158
194
  }
@@ -257,13 +293,13 @@ var KeystoneUsbConnectorBase = class {
257
293
  candidate = await this._openTransport(device2);
258
294
  const candidateConfig = await this._readAppConfig(candidate.transport);
259
295
  readableDeviceFound = true;
260
- if (_optionalChain([candidateConfig, 'access', _13 => _13.mfp, 'optionalAccess', _14 => _14.toLowerCase, 'call', _15 => _15()]) === expectedMasterFingerprint.toLowerCase()) {
296
+ if (_optionalChain([candidateConfig, 'access', _6 => _6.mfp, 'optionalAccess', _7 => _7.toLowerCase, 'call', _8 => _8()]) === expectedMasterFingerprint.toLowerCase()) {
261
297
  matched = { device: device2, opened: candidate, config: candidateConfig };
262
298
  break;
263
299
  }
264
300
  candidate.dispose();
265
301
  } catch (error) {
266
- _optionalChain([candidate, 'optionalAccess', _16 => _16.dispose, 'call', _17 => _17()]);
302
+ _optionalChain([candidate, 'optionalAccess', _9 => _9.dispose, 'call', _10 => _10()]);
267
303
  lastError = error;
268
304
  }
269
305
  }
@@ -336,9 +372,9 @@ var KeystoneUsbConnectorBase = class {
336
372
  connectId: sessionId,
337
373
  deviceId: "",
338
374
  name: selectedDevice ? usbDisplayName(selectedDevice) : "Keystone",
339
- model: _optionalChain([selectedDevice, 'optionalAccess', _18 => _18.productName]),
375
+ model: _optionalChain([selectedDevice, 'optionalAccess', _11 => _11.productName]),
340
376
  connectionType: "usb",
341
- serialNumber: _optionalChain([selectedDevice, 'optionalAccess', _19 => _19.serialNumber]),
377
+ serialNumber: _optionalChain([selectedDevice, 'optionalAccess', _12 => _12.serialNumber]),
342
378
  capabilities: { persistentDeviceIdentity: false },
343
379
  raw: { masterFingerprint: config.mfp }
344
380
  };
@@ -348,7 +384,7 @@ var KeystoneUsbConnectorBase = class {
348
384
  deviceInfo: this._toDeviceInfo(device, config.version, config.mfp)
349
385
  };
350
386
  } catch (err) {
351
- _optionalChain([openedTransport, 'optionalAccess', _20 => _20.dispose, 'call', _21 => _21()]);
387
+ _optionalChain([openedTransport, 'optionalAccess', _13 => _13.dispose, 'call', _14 => _14()]);
352
388
  throw mapKeystoneUsbError(err);
353
389
  }
354
390
  }
@@ -368,7 +404,7 @@ var KeystoneUsbConnectorBase = class {
368
404
  dispose: () => {
369
405
  if (disposed) return;
370
406
  disposed = true;
371
- _optionalChain([this, 'access', _22 => _22.transportClass, 'access', _23 => _23.disposeTransport, 'optionalCall', _24 => _24(transport)]);
407
+ _optionalChain([this, 'access', _15 => _15.transportClass, 'access', _16 => _16.disposeTransport, 'optionalCall', _17 => _17(transport)]);
372
408
  }
373
409
  };
374
410
  }
@@ -433,7 +469,7 @@ var KeystoneUsbConnectorBase = class {
433
469
  const { urType, urData } = params;
434
470
  const encoded = toUrEncoded({ urType, urData });
435
471
  const isPublicDataRequest = urType === KEYSTONE_PUBLIC_DATA_UR_TYPE;
436
- const publicDataKey = _nullishCoalesce(_optionalChain([session, 'access', _25 => _25.mfp, 'optionalAccess', _26 => _26.toLowerCase, 'call', _27 => _27()]), () => ( sessionId));
472
+ const publicDataKey = _nullishCoalesce(_optionalChain([session, 'access', _18 => _18.mfp, 'optionalAccess', _19 => _19.toLowerCase, 'call', _20 => _20()]), () => ( sessionId));
437
473
  const shouldShowConfirmation = !isPublicDataRequest || !this.publicDataConfirmedWallets.has(publicDataKey);
438
474
  if (shouldShowConfirmation) {
439
475
  this.emitter.emit("ui-event", {
@@ -551,4 +587,4 @@ var KeystoneUsbConnectorBase = class {
551
587
 
552
588
 
553
589
  exports.mapKeystoneUsbError = mapKeystoneUsbError; exports.KeystoneUsbConnectorBase = KeystoneUsbConnectorBase;
554
- //# sourceMappingURL=chunk-DMCM7F7V.js.map
590
+ //# sourceMappingURL=chunk-K6NZTBAU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-keystone-connector-usb/dist/chunk-K6NZTBAU.js","../src/errors.ts","../src/KeystoneUsbConnectorBase.ts"],"names":["HardwareErrorCode","createHwkError"],"mappings":"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","file":"/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-keystone-connector-usb/dist/chunk-K6NZTBAU.js","sourcesContent":[null,"import { HardwareErrorCode, createHwkError } from '@onekeyfe/hwk-adapter-core';\nimport { Status } from '@keystonehq/hw-transport-error';\n\nimport type { HwkError, HwkErrorOrigin } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Firmware status words occupy the low end of `Status` (0..15 today); client\n * codes start at `ERR_DEVICE_NOT_OPENED` (0xA0000001). Anything below that\n * boundary arrived inside a device response frame — see\n * `@keystonehq/hw-transport-webusb`, which throws\n * `TransportError(payload, result.status)` for every non-zero response status.\n */\nfunction isDeviceStatus(statusCode: number): boolean {\n return statusCode >= 0 && statusCode < Status.ERR_DEVICE_NOT_OPENED;\n}\n\n/**\n * Device-side outcomes (`Status` values reported inside a JSON response\n * payload's status word) — see EAPDU_Readme.md in the Keystone USB SDK repo.\n */\nconst DEVICE_STATUS_MAP: Partial<Record<number, HardwareErrorCode>> = {\n [Status.RSP_FAILURE_CODE]: HardwareErrorCode.UnknownError,\n [Status.PRS_INVALID_TOTAL_PACKETS]: HardwareErrorCode.InvalidParams,\n [Status.PRS_INVALID_INDEX]: HardwareErrorCode.InvalidParams,\n [Status.PRS_PARSING_REJECTED]: HardwareErrorCode.UserRejected,\n [Status.PRS_PARSING_ERROR]: HardwareErrorCode.InvalidParams,\n [Status.PRS_PARSING_DISALLOWED]: HardwareErrorCode.DeviceLocked,\n [Status.PRS_PARSING_UNMATCHED]: HardwareErrorCode.InvalidParams,\n [Status.PRS_PARSING_MISMATCHED_WALLET]: HardwareErrorCode.DeviceMismatch,\n [Status.PRS_PARSING_VERIFY_PASSWORD_ERROR]: HardwareErrorCode.PinInvalid,\n [Status.PRS_EXPORT_ADDRESS_UNSUPPORTED_CHAIN]: HardwareErrorCode.ChainNotSupported,\n [Status.PRS_EXPORT_ADDRESS_INVALID_PARAMS]: HardwareErrorCode.InvalidParams,\n [Status.PRS_EXPORT_ADDRESS_ERROR]: HardwareErrorCode.UnknownError,\n [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: HardwareErrorCode.DeviceLocked,\n [Status.PRS_EXPORT_ADDRESS_REJECTED]: HardwareErrorCode.UserRejected,\n [Status.PRS_EXPORT_ADDRESS_BUSY]: HardwareErrorCode.DeviceBusyInternal,\n};\n\n/**\n * The Keystone transport only names client-side codes (its `ErrorInfo`); for a\n * firmware status it forwards the device's response payload and falls back to a\n * bare 'unknown error'. These fill that gap when the device sent no text.\n */\nconst DEVICE_STATUS_MESSAGE: Partial<Record<number, string>> = {\n [Status.RSP_FAILURE_CODE]: 'Keystone reported a failure',\n [Status.PRS_INVALID_TOTAL_PACKETS]: 'Keystone rejected the request framing (total packets)',\n [Status.PRS_INVALID_INDEX]: 'Keystone rejected the request framing (packet index)',\n [Status.PRS_PARSING_REJECTED]: 'Rejected on the Keystone screen',\n [Status.PRS_PARSING_ERROR]: 'Keystone could not parse the request',\n [Status.PRS_PARSING_DISALLOWED]: 'Keystone declined the request in its current state',\n [Status.PRS_PARSING_UNMATCHED]: 'Keystone found no handler for the request',\n [Status.PRS_PARSING_MISMATCHED_WALLET]: 'The request belongs to a different Keystone wallet',\n [Status.PRS_PARSING_VERIFY_PASSWORD_ERROR]: 'Keystone password verification failed',\n [Status.PRS_EXPORT_ADDRESS_UNSUPPORTED_CHAIN]: 'Keystone does not support this chain',\n [Status.PRS_EXPORT_ADDRESS_INVALID_PARAMS]: 'Keystone rejected the address export parameters',\n [Status.PRS_EXPORT_ADDRESS_ERROR]: 'Keystone failed to export the address',\n [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: 'Keystone declined to export the address',\n [Status.PRS_EXPORT_ADDRESS_REJECTED]: 'Address export rejected on the Keystone screen',\n [Status.PRS_EXPORT_ADDRESS_BUSY]: 'Keystone is busy with another request',\n};\n\n/** The placeholder both Keystone transports use when they have nothing better. */\nconst PLACEHOLDER_MESSAGE = /^unknown error\\b/i;\n\n/** Client-side (transport/framing) failures — never reached the device. */\nconst CLIENT_STATUS_MAP: Partial<Record<number, HardwareErrorCode>> = {\n [Status.ERR_DEVICE_NOT_OPENED]: HardwareErrorCode.DeviceNotFound,\n [Status.ERR_DEVICE_NOT_FOUND]: HardwareErrorCode.DeviceNotFound,\n [Status.ERR_TIMEOUT]: HardwareErrorCode.OperationTimeout,\n [Status.ERR_DATA_TOO_LARGE]: HardwareErrorCode.PayloadTooLarge,\n [Status.ERR_NOT_SUPPORTED]: HardwareErrorCode.TransportNotAvailable,\n};\n\n/**\n * Maps a Keystone USB SDK failure to `HardwareErrorCode`. Errors that already\n * carry a numeric `.code` pass through unchanged. `transportErrorCode` is read\n * duck-typed because duplicate `@keystonehq/*` module instances break\n * `instanceof`.\n */\nexport function mapKeystoneUsbError(err: unknown): HwkError {\n const domName =\n err && typeof err === 'object' && typeof (err as { name?: unknown }).name === 'string'\n ? (err as { name: string }).name\n : undefined;\n\n // Legacy DOMException codes overlap the low-numbered browser error table\n // (for example NotFoundError is 8). Only five-digit HWK codes are already\n // mapped errors; letting any numeric `.code` pass through turns a WebUSB\n // disconnect into an unknown hardware error at the app boundary.\n if (\n err &&\n typeof err === 'object' &&\n typeof (err as { code?: unknown }).code === 'number' &&\n (err as { code: number }).code >= 10000 &&\n (err as { code: number }).code <= 99999\n ) {\n return err as HwkError;\n }\n\n const statusCode =\n err &&\n typeof err === 'object' &&\n typeof (err as { transportErrorCode?: unknown }).transportErrorCode === 'number'\n ? (err as { transportErrorCode: number }).transportErrorCode\n : undefined;\n\n const message = err instanceof Error ? err.message : String(err);\n\n if (statusCode !== undefined) {\n const fromDevice = isDeviceStatus(statusCode);\n // An unlisted firmware status is still the firmware answering, so it keeps\n // 'device' and only the code degrades. Guessing 'transport' there is what\n // makes KeystoneAdapter tear down a live USB session over an on-device\n // decline, and a real pipe death already arrives via the transport's\n // disconnect listener rather than a status word.\n const code = fromDevice\n ? DEVICE_STATUS_MAP[statusCode] ?? HardwareErrorCode.UnknownError\n : CLIENT_STATUS_MAP[statusCode] ?? HardwareErrorCode.TransportError;\n let origin: HwkErrorOrigin | undefined;\n if (fromDevice) {\n origin = 'device';\n } else if (code !== HardwareErrorCode.OperationTimeout) {\n origin = 'transport';\n }\n const description = DEVICE_STATUS_MESSAGE[statusCode];\n return createHwkError({\n code,\n // The device's own payload text wins; the table only covers the case\n // where the transport had nothing to forward.\n message:\n description && PLACEHOLDER_MESSAGE.test(message)\n ? `${description} (error_code: ${statusCode})`\n : message,\n // A firmware status word is a result, never a link problem. Client-side\n // status codes came from the framing/pipe layer — EXCEPT the timeout,\n // which is genuinely two-faced (the device may be sitting on a confirm\n // screen waiting for a human, or the pipe may be dead) and stays\n // unlabeled rather than mislabeled.\n origin,\n params: { statusCode, details: (err as { details?: string }).details },\n });\n }\n\n // WebUSB failures are DOMExceptions. The device dropping off the bus\n // mid-call is the common one — every command here is its own\n // open/claim/transfer/release/close cycle, so an unplug or a bus reset lands\n // exactly here — and it deserves a message the user can act on.\n if (domName === 'NotFoundError' || domName === 'NetworkError') {\n return createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message,\n origin: 'transport',\n params: { domExceptionName: domName },\n });\n }\n if (domName === 'SecurityError' || domName === 'NotAllowedError') {\n return createHwkError({\n code: HardwareErrorCode.DevicePermissionDenied,\n message,\n origin: 'host',\n params: { domExceptionName: domName },\n });\n }\n if (domName === 'InvalidStateError' || domName === 'AbortError') {\n return createHwkError({\n code: HardwareErrorCode.DeviceBusy,\n message,\n origin: 'transport',\n params: { domExceptionName: domName },\n });\n }\n\n // Anything else that reached this mapper was thrown by the transport layer\n // (this function only wraps USB transport calls), so 'transport' is a fact\n // about the throw site, not a guess about the cause.\n return createHwkError({ code: HardwareErrorCode.TransportError, message, origin: 'transport' });\n}\n","import { UR, URDecoder, UREncoder } from '@ngraveio/bc-ur';\nimport { Actions } from '@keystonehq/hw-transport-usb';\nimport {\n EConnectorInteraction,\n HardwareErrorCode,\n TypedEventEmitter,\n createHardwareLinkId,\n createHardwareSearchTargetId,\n createHwkError,\n hasHardwareRuntimeIdPrefix,\n parseBip32MasterFingerprint,\n parseHardwareRuntimeId,\n serializeConnectorError,\n success,\n} from '@onekeyfe/hwk-adapter-core';\n\nimport { mapKeystoneUsbError } from './errors';\n\nimport type {\n ConnectorCallResult,\n ConnectorConnectTarget,\n ConnectorDevice,\n ConnectorEventMap,\n ConnectorEventType,\n ConnectorSearchDevicesOptions,\n ConnectorSession,\n IConnector,\n UiResponseEvent,\n} from '@onekeyfe/hwk-adapter-core';\nimport type { TransportConfig, TransportHID } from '@keystonehq/hw-transport-usb';\n\n/**\n * Static surface shared by `TransportWebUSB` and `TransportNodeUSB`; the\n * platform-specific transport is injected by `_subpath/*`.\n */\n/**\n * What enumeration can see before the device is opened. WebUSB hands back a\n * `USBDevice`; node-usb's wrapper exposes the same descriptor strings. Every\n * field is optional because a device may simply not publish that string\n * descriptor — read defensively, never assume presence.\n */\nexport interface KeystoneUsbDeviceDescriptor {\n serialNumber?: string;\n productName?: string;\n manufacturerName?: string;\n vendorId?: number;\n productId?: number;\n /** Device release number (bcdDevice), split into its three nibble groups. */\n deviceVersionMajor?: number;\n deviceVersionMinor?: number;\n deviceVersionSubminor?: number;\n usbVersionMajor?: number;\n usbVersionMinor?: number;\n usbVersionSubminor?: number;\n deviceClass?: number;\n deviceSubclass?: number;\n deviceProtocol?: number;\n}\n\n/**\n * Enumeration-time display name. The serial is the only other field readable\n * before a device is opened, but it is not printed on the unit or shown in its\n * UI, so appending it cannot help anyone tell two Keystones apart — it only\n * makes the model harder to read.\n */\nfunction usbDisplayName(device: KeystoneUsbDeviceDescriptor): string {\n return device.productName?.trim() || 'Keystone';\n}\n\nexport interface KeystoneUsbTransportStatic {\n connect(config?: TransportConfig): Promise<TransportHID>;\n connectDevice?(\n device: KeystoneUsbDeviceDescriptor,\n config?: TransportConfig\n ): Promise<TransportHID>;\n getKeystoneDevices(): Promise<ReadonlyArray<KeystoneUsbDeviceDescriptor>>;\n requestPermission?(): Promise<void>;\n isSupported(): Promise<boolean>;\n /** Release transport-lifetime resources owned by the platform wrapper. */\n disposeTransport?(transport: TransportHID): void;\n}\n\ninterface KeystoneUr {\n urType: string;\n urData: string;\n}\n\ninterface UsbSession {\n transport: TransportHID;\n mfp?: string;\n dispose(): void;\n}\n\ninterface OpenedUsbTransport {\n transport: TransportHID;\n bindSessionId(sessionId: string): void;\n dispose(): void;\n}\n\ntype KeystoneUsbConnectInput =\n | { searchTargetId: string; expectedMasterFingerprint?: never }\n | { searchTargetId?: never; expectedMasterFingerprint: string }\n | { searchTargetId?: never; expectedMasterFingerprint?: never };\n\nconst DEFAULT_TIMEOUT_MS = 100_000; // SDK's own raw default is 15s (\"may need users' action on the device\") — too short for real confirmation.\n\nconst KEYSTONE_PUBLIC_DATA_UR_TYPE = 'qr-hardware-call';\n\nfunction toUrEncoded(ur: KeystoneUr): string {\n return new UREncoder(new UR(Buffer.from(ur.urData, 'hex'), ur.urType), Infinity)\n .nextPart()\n .toUpperCase();\n}\n\nfunction fromUrEncoded(encoded: unknown): KeystoneUr {\n // Accept either one complete UR or multiple whitespace-separated parts.\n // The transport owns EAPDU framing; this function only validates and joins\n // the BC-UR text present in that response.\n const text = typeof encoded === 'string' ? encoded.trim() : '';\n const parts = text.split(/\\s+/).filter(Boolean);\n const decoder = new URDecoder();\n for (const part of parts) {\n decoder.receivePart(part);\n if (decoder.isComplete()) break;\n }\n if (!parts.length || !decoder.isComplete() || !decoder.isSuccess()) {\n const shape =\n typeof encoded === 'string'\n ? `string len=${text.length} parts=${parts.length}`\n : typeof encoded;\n throw createHwkError({\n code: HardwareErrorCode.PayloadTooLarge,\n message: `Keystone USB returned an incomplete BC-UR response (${shape})`,\n origin: 'device',\n });\n }\n const ur = decoder.resultUR();\n return { urType: ur.type, urData: ur.cbor.toString('hex') };\n}\n\n/**\n * `IConnector` for Keystone over USB. Each transport call is its own\n * open/claim/transfer/close cycle, so `connect()` only caches the transport\n * object. `resolveUr` carries the same UR payloads as the QR channel.\n */\nexport class KeystoneUsbConnectorBase implements IConnector {\n readonly connectionType = 'usb' as const;\n\n private readonly transportClass: KeystoneUsbTransportStatic;\n\n private readonly timeoutMs: number;\n\n private readonly emitter = new TypedEventEmitter<ConnectorEventMap>();\n\n private readonly sessions = new Map<string, UsbSession>();\n\n private readonly discoveredDevices = new Map<string, KeystoneUsbDeviceDescriptor>();\n\n private readonly availabilityDevices = new Map<string, KeystoneUsbDeviceDescriptor>();\n\n private activeDiscoveryGeneration: symbol | undefined;\n\n private activeAvailabilityGeneration: symbol | undefined;\n\n // Keystone may re-enumerate on the USB bus between public-data exports,\n // which replaces the physical transport session without starting a new\n // user operation. Keep this UI-only state at connector lifetime scope so\n // an internal recovery does not look like another first connection.\n /** Wallets that already approved a public-data export, keyed by master fingerprint. */\n private readonly publicDataConfirmedWallets = new Set<string>();\n\n constructor(transportClass: KeystoneUsbTransportStatic, options?: { timeoutMs?: number }) {\n this.transportClass = transportClass;\n this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n private _invalidateDiscoverySnapshot(): void {\n this.activeDiscoveryGeneration = undefined;\n this.discoveredDevices.clear();\n }\n\n private _invalidateAvailabilitySnapshot(): void {\n this.activeAvailabilityGeneration = undefined;\n this.availabilityDevices.clear();\n }\n\n async searchDevices(options?: ConnectorSearchDevicesOptions): Promise<ConnectorDevice[]> {\n const isAvailabilitySearch = options?.purpose === 'availability';\n const discoveryGeneration = Symbol(\n isAvailabilitySearch ? 'keystone-usb-availability' : 'keystone-usb-discovery'\n );\n if (isAvailabilitySearch) {\n this._invalidateAvailabilitySnapshot();\n this.activeAvailabilityGeneration = discoveryGeneration;\n } else {\n this._invalidateDiscoverySnapshot();\n this.activeDiscoveryGeneration = discoveryGeneration;\n }\n // Enumeration throws DOMExceptions whose legacy `.code` collides with the\n // five-digit HWK range, so it needs the same mapping as every other\n // transport call here.\n let devices: ReadonlyArray<KeystoneUsbDeviceDescriptor>;\n try {\n devices = await this.transportClass.getKeystoneDevices();\n } catch (err) {\n throw mapKeystoneUsbError(err);\n }\n // No mfp is available without opening+claiming the device. The target id\n // therefore identifies only this discovery snapshot; wallet identity is\n // learned and verified after connect. WebUSB can reopen the exact cached\n // descriptor. Platforms without that ability fail closed for multi-device\n // selection instead of silently opening a different unit.\n if (\n (isAvailabilitySearch\n ? this.activeAvailabilityGeneration\n : this.activeDiscoveryGeneration) !== discoveryGeneration\n ) {\n return [];\n }\n return devices.map(device => {\n const connectId = createHardwareSearchTargetId('keystone');\n (isAvailabilitySearch ? this.availabilityDevices : this.discoveredDevices).set(\n connectId,\n device\n );\n return {\n connectId,\n deviceId: '',\n name: usbDisplayName(device),\n connectionType: 'usb',\n serialNumber: device.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n // Everything enumeration can see, verbatim. Nothing here is normalized\n // or defaulted: a missing key means the device published no such string\n // descriptor. Promote a field onto the typed surface above once\n // something actually consumes it.\n raw: {\n serialNumber: device.serialNumber,\n productName: device.productName,\n manufacturerName: device.manufacturerName,\n vendorId: device.vendorId,\n productId: device.productId,\n deviceVersionMajor: device.deviceVersionMajor,\n deviceVersionMinor: device.deviceVersionMinor,\n deviceVersionSubminor: device.deviceVersionSubminor,\n usbVersionMajor: device.usbVersionMajor,\n usbVersionMinor: device.usbVersionMinor,\n usbVersionSubminor: device.usbVersionSubminor,\n deviceClass: device.deviceClass,\n deviceSubclass: device.deviceSubclass,\n deviceProtocol: device.deviceProtocol,\n },\n };\n });\n }\n\n async connect(searchTargetIdOrExpectedMasterFingerprint?: string): Promise<ConnectorSession> {\n return this._connectResolved(\n this._resolveConnectInput(searchTargetIdOrExpectedMasterFingerprint)\n );\n }\n\n async connectTarget(target: ConnectorConnectTarget): Promise<ConnectorSession> {\n switch (target.type) {\n case 'default':\n return this._connectResolved({});\n case 'search-target':\n return this._connectResolved({ searchTargetId: target.searchTargetId });\n case 'expected-device-identity': {\n const expectedMasterFingerprint = parseBip32MasterFingerprint(target.deviceIdentity);\n if (!expectedMasterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector requires a valid master fingerprint identity',\n });\n }\n return this._connectResolved({ expectedMasterFingerprint });\n }\n default:\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Unsupported Keystone USB connection target',\n });\n }\n }\n\n private async _connectResolved({\n searchTargetId,\n expectedMasterFingerprint,\n }: KeystoneUsbConnectInput): Promise<ConnectorSession> {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.Searching,\n payload: { sessionId: '' },\n });\n let selectedDevice = searchTargetId\n ? this.discoveredDevices.get(searchTargetId) ?? this.availabilityDevices.get(searchTargetId)\n : undefined;\n if (searchTargetId && !selectedDevice) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: `Keystone USB search target is not in the current scan: ${searchTargetId}`,\n recovery: { scope: 'search-target' },\n });\n }\n let openedTransport: OpenedUsbTransport | undefined;\n try {\n let config: Awaited<ReturnType<KeystoneUsbConnectorBase['_readAppConfig']>>;\n\n if (expectedMasterFingerprint && !searchTargetId) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (!this.transportClass.connectDevice && availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'This platform cannot identify one of multiple connected Keystone devices',\n });\n }\n\n if (this.transportClass.connectDevice) {\n let readableDeviceFound = false;\n let lastError: unknown;\n let matched:\n | {\n device: KeystoneUsbDeviceDescriptor;\n opened: OpenedUsbTransport;\n config: Awaited<ReturnType<KeystoneUsbConnectorBase['_readAppConfig']>>;\n }\n | undefined;\n for (const device of availableDevices) {\n let candidate: OpenedUsbTransport | undefined;\n try {\n // eslint-disable-next-line no-await-in-loop\n candidate = await this._openTransport(device);\n // eslint-disable-next-line no-await-in-loop\n const candidateConfig = await this._readAppConfig(candidate.transport);\n readableDeviceFound = true;\n if (candidateConfig.mfp?.toLowerCase() === expectedMasterFingerprint.toLowerCase()) {\n matched = { device, opened: candidate, config: candidateConfig };\n break;\n }\n candidate.dispose();\n } catch (error) {\n candidate?.dispose();\n lastError = error;\n }\n }\n if (!matched) {\n if (!readableDeviceFound && lastError) throw mapKeystoneUsbError(lastError);\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message: `No connected Keystone wallet matches fingerprint ${expectedMasterFingerprint}`,\n });\n }\n selectedDevice = matched.device;\n openedTransport = matched.opened;\n config = matched.config;\n } else {\n openedTransport = await this._openTransport();\n config = await this._readAppConfig(openedTransport.transport);\n }\n } else {\n if (!selectedDevice && this.transportClass.connectDevice) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (availableDevices.length === 0) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: 'No Keystone USB device is available',\n });\n }\n if (availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'Select a Keystone USB device before connecting',\n });\n }\n [selectedDevice] = availableDevices;\n }\n if (selectedDevice && !this.transportClass.connectDevice) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'This platform cannot target one of multiple connected Keystone devices',\n });\n }\n }\n openedTransport = await this._openTransport(selectedDevice);\n config = await this._readAppConfig(openedTransport.transport);\n }\n\n if (!openedTransport) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: 'Keystone USB transport was not opened',\n });\n }\n const activeOpenedTransport = openedTransport;\n const { transport } = activeOpenedTransport;\n if (\n expectedMasterFingerprint &&\n config.mfp &&\n config.mfp.toLowerCase() !== expectedMasterFingerprint.toLowerCase()\n ) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message: `Connected Keystone wallet (mfp ${config.mfp}) does not match the requested wallet fingerprint (${expectedMasterFingerprint})`,\n });\n }\n const sessionId = createHardwareLinkId('keystone');\n activeOpenedTransport.bindSessionId(sessionId);\n this.sessions.set(sessionId, {\n transport,\n mfp: config.mfp,\n dispose: () => activeOpenedTransport.dispose(),\n });\n openedTransport = undefined;\n const device: ConnectorDevice = {\n connectId: sessionId,\n deviceId: '',\n name: selectedDevice ? usbDisplayName(selectedDevice) : 'Keystone',\n model: selectedDevice?.productName,\n connectionType: 'usb',\n serialNumber: selectedDevice?.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n raw: { masterFingerprint: config.mfp },\n };\n this.emitter.emit('device-connect', { device });\n return {\n sessionId,\n deviceInfo: this._toDeviceInfo(device, config.version, config.mfp),\n };\n } catch (err) {\n openedTransport?.dispose();\n throw mapKeystoneUsbError(err);\n }\n }\n\n private async _openTransport(device?: KeystoneUsbDeviceDescriptor): Promise<OpenedUsbTransport> {\n // A rejected candidate can keep its USB disconnect listener. Give every\n // descriptor a separate holder so it cannot retire a later matched session.\n let connectedSessionId: string | undefined;\n const transportConfig: TransportConfig = {\n timeout: this.timeoutMs,\n disconnectListener: () =>\n connectedSessionId ? this.disconnect(connectedSessionId) : undefined,\n };\n const transport = await (device && this.transportClass.connectDevice\n ? this.transportClass.connectDevice(device, transportConfig)\n : this.transportClass.connect(transportConfig));\n let disposed = false;\n return {\n transport,\n bindSessionId(sessionId: string) {\n connectedSessionId = sessionId;\n },\n dispose: () => {\n if (disposed) return;\n disposed = true;\n this.transportClass.disposeTransport?.(transport);\n },\n };\n }\n\n private _resolveConnectInput(value?: string): KeystoneUsbConnectInput {\n if (!value) return {};\n\n const runtimeId = parseHardwareRuntimeId(value);\n if (runtimeId) {\n // Whether this particular target is ours is settled by the snapshot\n // lookup in `_connectResolved`, not by anything encoded in the id.\n if (runtimeId.kind === 'search-target' && runtimeId.vendor === 'keystone') {\n return { searchTargetId: value };\n }\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector received an incompatible hardware runtime id',\n params: { kind: runtimeId.kind, vendor: runtimeId.vendor },\n });\n }\n\n if (hasHardwareRuntimeIdPrefix(value)) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector received a malformed hardware runtime id',\n });\n }\n\n const expectedMasterFingerprint = parseBip32MasterFingerprint(value);\n if (!expectedMasterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector requires a search target or master fingerprint',\n });\n }\n return { expectedMasterFingerprint };\n }\n\n disconnect(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n this.sessions.delete(sessionId);\n if (session) {\n session.dispose();\n // No persistent claim to release (see class doc) — nothing to await\n // here beyond letting the underlying transport's own per-call close\n // run its course, which already happened on the last send().\n this.emitter.emit('device-disconnect', { connectId: sessionId });\n }\n return Promise.resolve();\n }\n\n /** Keep the vendor transport as the EAPDU framing authority. */\n private async _sendResolveUr(transport: TransportHID, encodedUr: string): Promise<KeystoneUr> {\n const response = await transport.send<{ payload: string }>(Actions.CMD_RESOLVE_UR, encodedUr);\n return fromUrEncoded(response.payload);\n }\n\n async call(sessionId: string, method: string, params: unknown): Promise<ConnectorCallResult> {\n const session = this.sessions.get(sessionId);\n if (!session) {\n return {\n success: false,\n error: serializeConnectorError(\n createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: `Unknown Keystone USB session: ${sessionId}`,\n })\n ),\n };\n }\n\n try {\n switch (method) {\n case 'resolveUr': {\n const { urType, urData } = params as KeystoneUr;\n const encoded = toUrEncoded({ urType, urData });\n const isPublicDataRequest = urType === KEYSTONE_PUBLIC_DATA_UR_TYPE;\n // Public-data export asks for approval once per wallet: internal USB\n // re-enumeration must not reopen the toast, but a different device\n // has not approved anything yet. Signing always confirms.\n const publicDataKey = session.mfp?.toLowerCase() ?? sessionId;\n const shouldShowConfirmation =\n !isPublicDataRequest || !this.publicDataConfirmedWallets.has(publicDataKey);\n if (shouldShowConfirmation) {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.ConfirmOnDevice,\n payload: { sessionId },\n });\n }\n try {\n const response = await this._sendResolveUr(session.transport, encoded);\n if (isPublicDataRequest) {\n this.publicDataConfirmedWallets.add(publicDataKey);\n }\n return success(response);\n } finally {\n if (shouldShowConfirmation) {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.InteractionComplete,\n payload: { sessionId },\n });\n }\n }\n }\n case 'checkLockStatus': {\n const response = await session.transport.send<{ payload: boolean }>(\n Actions.CMD_CHECK_LOCK_STATUS,\n ''\n );\n // Naming-only assumption (no positive-case fixture to confirm\n // against) — `checkDeviceLockStatus`'s own name is the only\n // evidence `true` means locked; verify against real hardware.\n return success({ locked: response.payload });\n }\n case 'getAppConfig': {\n const config = await this._readAppConfig(session.transport);\n return success(config);\n }\n default:\n return {\n success: false,\n error: serializeConnectorError(\n createHwkError({\n code: HardwareErrorCode.MethodNotSupported,\n message: `Unknown Keystone USB connector method: ${method}`,\n })\n ),\n };\n }\n } catch (err) {\n return { success: false, error: serializeConnectorError(mapKeystoneUsbError(err)) };\n }\n }\n\n // No protocol-level cancel exists over USB (verified: nothing in the SDK's\n // public surface can interrupt an in-flight transferIn/transferOut) — the\n // best this connector can do is stop waiting on its side; the underlying\n // `send()` promise still settles (or times out) on its own.\n cancel(_sessionId: string): Promise<void> {\n return Promise.resolve();\n }\n\n // Keystone USB never relays PIN/passphrase through the host — entry always\n // happens on the device's own touchscreen — so there is nothing to answer.\n uiResponse(_response: UiResponseEvent): void {}\n\n on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void {\n this.emitter.on(event, handler);\n }\n\n off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void {\n this.emitter.off(event, handler);\n }\n\n reset(): void {\n for (const session of this.sessions.values()) {\n session.dispose();\n }\n this.sessions.clear();\n this._invalidateDiscoverySnapshot();\n this._invalidateAvailabilitySnapshot();\n this.publicDataConfirmedWallets.clear();\n }\n\n private async _readAppConfig(\n transport: TransportHID\n ): Promise<{ version?: string; mfp?: string }> {\n const response = await transport.send<Record<string, unknown>>(\n Actions.CMD_GET_DEVICE_VERSION,\n ''\n );\n const rawMasterFingerprint = response.walletMFP;\n const masterFingerprint = parseBip32MasterFingerprint(rawMasterFingerprint);\n if (rawMasterFingerprint !== undefined && !masterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message:\n 'Keystone returned an invalid master fingerprint; expected exactly 4 bytes (8 hex characters)',\n });\n }\n return {\n version: typeof response.firmwareVersion === 'string' ? response.firmwareVersion : undefined,\n mfp: masterFingerprint,\n };\n }\n\n private _toDeviceInfo(\n device: ConnectorDevice,\n firmwareVersion?: string,\n masterFingerprint?: string\n ) {\n return {\n vendor: 'keystone' as const,\n // The device publishes its model as a USB string descriptor\n // (`productName`, e.g. \"Keystone 3 Pro\"); `device.model` carries it here.\n // Empty rather than a literal 'unknown' when it says nothing, so the\n // host's own default-name fallback can win instead of a fake model\n // reaching the UI and the persisted device settings.\n model: device.model ?? '',\n modelName: device.modelName,\n firmwareVersion: firmwareVersion ?? '0.0.0',\n deviceId: device.deviceId,\n connectId: device.connectId,\n connectionType: 'usb' as const,\n serialNumber: device.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n raw: { masterFingerprint },\n };\n }\n}\n"]}
@@ -1,13 +1,44 @@
1
1
  // src/errors.ts
2
2
  import { HardwareErrorCode, createHwkError } from "@onekeyfe/hwk-adapter-core";
3
3
  import { Status } from "@keystonehq/hw-transport-error";
4
+ function isDeviceStatus(statusCode) {
5
+ return statusCode >= 0 && statusCode < Status.ERR_DEVICE_NOT_OPENED;
6
+ }
4
7
  var DEVICE_STATUS_MAP = {
8
+ [Status.RSP_FAILURE_CODE]: HardwareErrorCode.UnknownError,
9
+ [Status.PRS_INVALID_TOTAL_PACKETS]: HardwareErrorCode.InvalidParams,
10
+ [Status.PRS_INVALID_INDEX]: HardwareErrorCode.InvalidParams,
5
11
  [Status.PRS_PARSING_REJECTED]: HardwareErrorCode.UserRejected,
12
+ [Status.PRS_PARSING_ERROR]: HardwareErrorCode.InvalidParams,
6
13
  [Status.PRS_PARSING_DISALLOWED]: HardwareErrorCode.DeviceLocked,
14
+ [Status.PRS_PARSING_UNMATCHED]: HardwareErrorCode.InvalidParams,
7
15
  [Status.PRS_PARSING_MISMATCHED_WALLET]: HardwareErrorCode.DeviceMismatch,
16
+ [Status.PRS_PARSING_VERIFY_PASSWORD_ERROR]: HardwareErrorCode.PinInvalid,
17
+ [Status.PRS_EXPORT_ADDRESS_UNSUPPORTED_CHAIN]: HardwareErrorCode.ChainNotSupported,
18
+ [Status.PRS_EXPORT_ADDRESS_INVALID_PARAMS]: HardwareErrorCode.InvalidParams,
19
+ [Status.PRS_EXPORT_ADDRESS_ERROR]: HardwareErrorCode.UnknownError,
20
+ [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: HardwareErrorCode.DeviceLocked,
8
21
  [Status.PRS_EXPORT_ADDRESS_REJECTED]: HardwareErrorCode.UserRejected,
9
- [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: HardwareErrorCode.DeviceLocked
22
+ [Status.PRS_EXPORT_ADDRESS_BUSY]: HardwareErrorCode.DeviceBusyInternal
23
+ };
24
+ var DEVICE_STATUS_MESSAGE = {
25
+ [Status.RSP_FAILURE_CODE]: "Keystone reported a failure",
26
+ [Status.PRS_INVALID_TOTAL_PACKETS]: "Keystone rejected the request framing (total packets)",
27
+ [Status.PRS_INVALID_INDEX]: "Keystone rejected the request framing (packet index)",
28
+ [Status.PRS_PARSING_REJECTED]: "Rejected on the Keystone screen",
29
+ [Status.PRS_PARSING_ERROR]: "Keystone could not parse the request",
30
+ [Status.PRS_PARSING_DISALLOWED]: "Keystone declined the request in its current state",
31
+ [Status.PRS_PARSING_UNMATCHED]: "Keystone found no handler for the request",
32
+ [Status.PRS_PARSING_MISMATCHED_WALLET]: "The request belongs to a different Keystone wallet",
33
+ [Status.PRS_PARSING_VERIFY_PASSWORD_ERROR]: "Keystone password verification failed",
34
+ [Status.PRS_EXPORT_ADDRESS_UNSUPPORTED_CHAIN]: "Keystone does not support this chain",
35
+ [Status.PRS_EXPORT_ADDRESS_INVALID_PARAMS]: "Keystone rejected the address export parameters",
36
+ [Status.PRS_EXPORT_ADDRESS_ERROR]: "Keystone failed to export the address",
37
+ [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: "Keystone declined to export the address",
38
+ [Status.PRS_EXPORT_ADDRESS_REJECTED]: "Address export rejected on the Keystone screen",
39
+ [Status.PRS_EXPORT_ADDRESS_BUSY]: "Keystone is busy with another request"
10
40
  };
41
+ var PLACEHOLDER_MESSAGE = /^unknown error\b/i;
11
42
  var CLIENT_STATUS_MAP = {
12
43
  [Status.ERR_DEVICE_NOT_OPENED]: HardwareErrorCode.DeviceNotFound,
13
44
  [Status.ERR_DEVICE_NOT_FOUND]: HardwareErrorCode.DeviceNotFound,
@@ -23,19 +54,21 @@ function mapKeystoneUsbError(err) {
23
54
  const statusCode = err && typeof err === "object" && typeof err.transportErrorCode === "number" ? err.transportErrorCode : void 0;
24
55
  const message = err instanceof Error ? err.message : String(err);
25
56
  if (statusCode !== void 0) {
26
- const deviceCode = DEVICE_STATUS_MAP[statusCode];
27
- const code = deviceCode ?? CLIENT_STATUS_MAP[statusCode] ?? HardwareErrorCode.TransportError;
57
+ const fromDevice = isDeviceStatus(statusCode);
58
+ const code = fromDevice ? DEVICE_STATUS_MAP[statusCode] ?? HardwareErrorCode.UnknownError : CLIENT_STATUS_MAP[statusCode] ?? HardwareErrorCode.TransportError;
28
59
  let origin;
29
- if (deviceCode !== void 0) {
60
+ if (fromDevice) {
30
61
  origin = "device";
31
62
  } else if (code !== HardwareErrorCode.OperationTimeout) {
32
63
  origin = "transport";
33
64
  }
65
+ const description = DEVICE_STATUS_MESSAGE[statusCode];
34
66
  return createHwkError({
35
67
  code,
36
- message,
37
- // A DEVICE_STATUS_MAP hit is the firmware answering (rejection, locked,
38
- // mismatched wallet) a result, never a link problem. Client-side
68
+ // The device's own payload text wins; the table only covers the case
69
+ // where the transport had nothing to forward.
70
+ message: description && PLACEHOLDER_MESSAGE.test(message) ? `${description} (error_code: ${statusCode})` : message,
71
+ // A firmware status word is a result, never a link problem. Client-side
39
72
  // status codes came from the framing/pipe layer — EXCEPT the timeout,
40
73
  // which is genuinely two-faced (the device may be sitting on a confirm
41
74
  // screen waiting for a human, or the pipe may be dead) and stays
@@ -88,9 +121,7 @@ import {
88
121
  success
89
122
  } from "@onekeyfe/hwk-adapter-core";
90
123
  function usbDisplayName(device) {
91
- const base = device.productName?.trim() || "Keystone";
92
- const suffix = device.serialNumber?.split("-").pop()?.trim();
93
- return suffix ? `${base} ${suffix}` : base;
124
+ return device.productName?.trim() || "Keystone";
94
125
  }
95
126
  var DEFAULT_TIMEOUT_MS = 1e5;
96
127
  var KEYSTONE_PUBLIC_DATA_UR_TYPE = "qr-hardware-call";
@@ -152,7 +183,12 @@ var KeystoneUsbConnectorBase = class {
152
183
  this._invalidateDiscoverySnapshot();
153
184
  this.activeDiscoveryGeneration = discoveryGeneration;
154
185
  }
155
- const devices = await this.transportClass.getKeystoneDevices();
186
+ let devices;
187
+ try {
188
+ devices = await this.transportClass.getKeystoneDevices();
189
+ } catch (err) {
190
+ throw mapKeystoneUsbError(err);
191
+ }
156
192
  if ((isAvailabilitySearch ? this.activeAvailabilityGeneration : this.activeDiscoveryGeneration) !== discoveryGeneration) {
157
193
  return [];
158
194
  }
@@ -551,4 +587,4 @@ export {
551
587
  mapKeystoneUsbError,
552
588
  KeystoneUsbConnectorBase
553
589
  };
554
- //# sourceMappingURL=chunk-2B6OTW7M.mjs.map
590
+ //# sourceMappingURL=chunk-U7YVTDVR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/KeystoneUsbConnectorBase.ts"],"sourcesContent":["import { HardwareErrorCode, createHwkError } from '@onekeyfe/hwk-adapter-core';\nimport { Status } from '@keystonehq/hw-transport-error';\n\nimport type { HwkError, HwkErrorOrigin } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Firmware status words occupy the low end of `Status` (0..15 today); client\n * codes start at `ERR_DEVICE_NOT_OPENED` (0xA0000001). Anything below that\n * boundary arrived inside a device response frame — see\n * `@keystonehq/hw-transport-webusb`, which throws\n * `TransportError(payload, result.status)` for every non-zero response status.\n */\nfunction isDeviceStatus(statusCode: number): boolean {\n return statusCode >= 0 && statusCode < Status.ERR_DEVICE_NOT_OPENED;\n}\n\n/**\n * Device-side outcomes (`Status` values reported inside a JSON response\n * payload's status word) — see EAPDU_Readme.md in the Keystone USB SDK repo.\n */\nconst DEVICE_STATUS_MAP: Partial<Record<number, HardwareErrorCode>> = {\n [Status.RSP_FAILURE_CODE]: HardwareErrorCode.UnknownError,\n [Status.PRS_INVALID_TOTAL_PACKETS]: HardwareErrorCode.InvalidParams,\n [Status.PRS_INVALID_INDEX]: HardwareErrorCode.InvalidParams,\n [Status.PRS_PARSING_REJECTED]: HardwareErrorCode.UserRejected,\n [Status.PRS_PARSING_ERROR]: HardwareErrorCode.InvalidParams,\n [Status.PRS_PARSING_DISALLOWED]: HardwareErrorCode.DeviceLocked,\n [Status.PRS_PARSING_UNMATCHED]: HardwareErrorCode.InvalidParams,\n [Status.PRS_PARSING_MISMATCHED_WALLET]: HardwareErrorCode.DeviceMismatch,\n [Status.PRS_PARSING_VERIFY_PASSWORD_ERROR]: HardwareErrorCode.PinInvalid,\n [Status.PRS_EXPORT_ADDRESS_UNSUPPORTED_CHAIN]: HardwareErrorCode.ChainNotSupported,\n [Status.PRS_EXPORT_ADDRESS_INVALID_PARAMS]: HardwareErrorCode.InvalidParams,\n [Status.PRS_EXPORT_ADDRESS_ERROR]: HardwareErrorCode.UnknownError,\n [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: HardwareErrorCode.DeviceLocked,\n [Status.PRS_EXPORT_ADDRESS_REJECTED]: HardwareErrorCode.UserRejected,\n [Status.PRS_EXPORT_ADDRESS_BUSY]: HardwareErrorCode.DeviceBusyInternal,\n};\n\n/**\n * The Keystone transport only names client-side codes (its `ErrorInfo`); for a\n * firmware status it forwards the device's response payload and falls back to a\n * bare 'unknown error'. These fill that gap when the device sent no text.\n */\nconst DEVICE_STATUS_MESSAGE: Partial<Record<number, string>> = {\n [Status.RSP_FAILURE_CODE]: 'Keystone reported a failure',\n [Status.PRS_INVALID_TOTAL_PACKETS]: 'Keystone rejected the request framing (total packets)',\n [Status.PRS_INVALID_INDEX]: 'Keystone rejected the request framing (packet index)',\n [Status.PRS_PARSING_REJECTED]: 'Rejected on the Keystone screen',\n [Status.PRS_PARSING_ERROR]: 'Keystone could not parse the request',\n [Status.PRS_PARSING_DISALLOWED]: 'Keystone declined the request in its current state',\n [Status.PRS_PARSING_UNMATCHED]: 'Keystone found no handler for the request',\n [Status.PRS_PARSING_MISMATCHED_WALLET]: 'The request belongs to a different Keystone wallet',\n [Status.PRS_PARSING_VERIFY_PASSWORD_ERROR]: 'Keystone password verification failed',\n [Status.PRS_EXPORT_ADDRESS_UNSUPPORTED_CHAIN]: 'Keystone does not support this chain',\n [Status.PRS_EXPORT_ADDRESS_INVALID_PARAMS]: 'Keystone rejected the address export parameters',\n [Status.PRS_EXPORT_ADDRESS_ERROR]: 'Keystone failed to export the address',\n [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: 'Keystone declined to export the address',\n [Status.PRS_EXPORT_ADDRESS_REJECTED]: 'Address export rejected on the Keystone screen',\n [Status.PRS_EXPORT_ADDRESS_BUSY]: 'Keystone is busy with another request',\n};\n\n/** The placeholder both Keystone transports use when they have nothing better. */\nconst PLACEHOLDER_MESSAGE = /^unknown error\\b/i;\n\n/** Client-side (transport/framing) failures — never reached the device. */\nconst CLIENT_STATUS_MAP: Partial<Record<number, HardwareErrorCode>> = {\n [Status.ERR_DEVICE_NOT_OPENED]: HardwareErrorCode.DeviceNotFound,\n [Status.ERR_DEVICE_NOT_FOUND]: HardwareErrorCode.DeviceNotFound,\n [Status.ERR_TIMEOUT]: HardwareErrorCode.OperationTimeout,\n [Status.ERR_DATA_TOO_LARGE]: HardwareErrorCode.PayloadTooLarge,\n [Status.ERR_NOT_SUPPORTED]: HardwareErrorCode.TransportNotAvailable,\n};\n\n/**\n * Maps a Keystone USB SDK failure to `HardwareErrorCode`. Errors that already\n * carry a numeric `.code` pass through unchanged. `transportErrorCode` is read\n * duck-typed because duplicate `@keystonehq/*` module instances break\n * `instanceof`.\n */\nexport function mapKeystoneUsbError(err: unknown): HwkError {\n const domName =\n err && typeof err === 'object' && typeof (err as { name?: unknown }).name === 'string'\n ? (err as { name: string }).name\n : undefined;\n\n // Legacy DOMException codes overlap the low-numbered browser error table\n // (for example NotFoundError is 8). Only five-digit HWK codes are already\n // mapped errors; letting any numeric `.code` pass through turns a WebUSB\n // disconnect into an unknown hardware error at the app boundary.\n if (\n err &&\n typeof err === 'object' &&\n typeof (err as { code?: unknown }).code === 'number' &&\n (err as { code: number }).code >= 10000 &&\n (err as { code: number }).code <= 99999\n ) {\n return err as HwkError;\n }\n\n const statusCode =\n err &&\n typeof err === 'object' &&\n typeof (err as { transportErrorCode?: unknown }).transportErrorCode === 'number'\n ? (err as { transportErrorCode: number }).transportErrorCode\n : undefined;\n\n const message = err instanceof Error ? err.message : String(err);\n\n if (statusCode !== undefined) {\n const fromDevice = isDeviceStatus(statusCode);\n // An unlisted firmware status is still the firmware answering, so it keeps\n // 'device' and only the code degrades. Guessing 'transport' there is what\n // makes KeystoneAdapter tear down a live USB session over an on-device\n // decline, and a real pipe death already arrives via the transport's\n // disconnect listener rather than a status word.\n const code = fromDevice\n ? DEVICE_STATUS_MAP[statusCode] ?? HardwareErrorCode.UnknownError\n : CLIENT_STATUS_MAP[statusCode] ?? HardwareErrorCode.TransportError;\n let origin: HwkErrorOrigin | undefined;\n if (fromDevice) {\n origin = 'device';\n } else if (code !== HardwareErrorCode.OperationTimeout) {\n origin = 'transport';\n }\n const description = DEVICE_STATUS_MESSAGE[statusCode];\n return createHwkError({\n code,\n // The device's own payload text wins; the table only covers the case\n // where the transport had nothing to forward.\n message:\n description && PLACEHOLDER_MESSAGE.test(message)\n ? `${description} (error_code: ${statusCode})`\n : message,\n // A firmware status word is a result, never a link problem. Client-side\n // status codes came from the framing/pipe layer — EXCEPT the timeout,\n // which is genuinely two-faced (the device may be sitting on a confirm\n // screen waiting for a human, or the pipe may be dead) and stays\n // unlabeled rather than mislabeled.\n origin,\n params: { statusCode, details: (err as { details?: string }).details },\n });\n }\n\n // WebUSB failures are DOMExceptions. The device dropping off the bus\n // mid-call is the common one — every command here is its own\n // open/claim/transfer/release/close cycle, so an unplug or a bus reset lands\n // exactly here — and it deserves a message the user can act on.\n if (domName === 'NotFoundError' || domName === 'NetworkError') {\n return createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message,\n origin: 'transport',\n params: { domExceptionName: domName },\n });\n }\n if (domName === 'SecurityError' || domName === 'NotAllowedError') {\n return createHwkError({\n code: HardwareErrorCode.DevicePermissionDenied,\n message,\n origin: 'host',\n params: { domExceptionName: domName },\n });\n }\n if (domName === 'InvalidStateError' || domName === 'AbortError') {\n return createHwkError({\n code: HardwareErrorCode.DeviceBusy,\n message,\n origin: 'transport',\n params: { domExceptionName: domName },\n });\n }\n\n // Anything else that reached this mapper was thrown by the transport layer\n // (this function only wraps USB transport calls), so 'transport' is a fact\n // about the throw site, not a guess about the cause.\n return createHwkError({ code: HardwareErrorCode.TransportError, message, origin: 'transport' });\n}\n","import { UR, URDecoder, UREncoder } from '@ngraveio/bc-ur';\nimport { Actions } from '@keystonehq/hw-transport-usb';\nimport {\n EConnectorInteraction,\n HardwareErrorCode,\n TypedEventEmitter,\n createHardwareLinkId,\n createHardwareSearchTargetId,\n createHwkError,\n hasHardwareRuntimeIdPrefix,\n parseBip32MasterFingerprint,\n parseHardwareRuntimeId,\n serializeConnectorError,\n success,\n} from '@onekeyfe/hwk-adapter-core';\n\nimport { mapKeystoneUsbError } from './errors';\n\nimport type {\n ConnectorCallResult,\n ConnectorConnectTarget,\n ConnectorDevice,\n ConnectorEventMap,\n ConnectorEventType,\n ConnectorSearchDevicesOptions,\n ConnectorSession,\n IConnector,\n UiResponseEvent,\n} from '@onekeyfe/hwk-adapter-core';\nimport type { TransportConfig, TransportHID } from '@keystonehq/hw-transport-usb';\n\n/**\n * Static surface shared by `TransportWebUSB` and `TransportNodeUSB`; the\n * platform-specific transport is injected by `_subpath/*`.\n */\n/**\n * What enumeration can see before the device is opened. WebUSB hands back a\n * `USBDevice`; node-usb's wrapper exposes the same descriptor strings. Every\n * field is optional because a device may simply not publish that string\n * descriptor — read defensively, never assume presence.\n */\nexport interface KeystoneUsbDeviceDescriptor {\n serialNumber?: string;\n productName?: string;\n manufacturerName?: string;\n vendorId?: number;\n productId?: number;\n /** Device release number (bcdDevice), split into its three nibble groups. */\n deviceVersionMajor?: number;\n deviceVersionMinor?: number;\n deviceVersionSubminor?: number;\n usbVersionMajor?: number;\n usbVersionMinor?: number;\n usbVersionSubminor?: number;\n deviceClass?: number;\n deviceSubclass?: number;\n deviceProtocol?: number;\n}\n\n/**\n * Enumeration-time display name. The serial is the only other field readable\n * before a device is opened, but it is not printed on the unit or shown in its\n * UI, so appending it cannot help anyone tell two Keystones apart — it only\n * makes the model harder to read.\n */\nfunction usbDisplayName(device: KeystoneUsbDeviceDescriptor): string {\n return device.productName?.trim() || 'Keystone';\n}\n\nexport interface KeystoneUsbTransportStatic {\n connect(config?: TransportConfig): Promise<TransportHID>;\n connectDevice?(\n device: KeystoneUsbDeviceDescriptor,\n config?: TransportConfig\n ): Promise<TransportHID>;\n getKeystoneDevices(): Promise<ReadonlyArray<KeystoneUsbDeviceDescriptor>>;\n requestPermission?(): Promise<void>;\n isSupported(): Promise<boolean>;\n /** Release transport-lifetime resources owned by the platform wrapper. */\n disposeTransport?(transport: TransportHID): void;\n}\n\ninterface KeystoneUr {\n urType: string;\n urData: string;\n}\n\ninterface UsbSession {\n transport: TransportHID;\n mfp?: string;\n dispose(): void;\n}\n\ninterface OpenedUsbTransport {\n transport: TransportHID;\n bindSessionId(sessionId: string): void;\n dispose(): void;\n}\n\ntype KeystoneUsbConnectInput =\n | { searchTargetId: string; expectedMasterFingerprint?: never }\n | { searchTargetId?: never; expectedMasterFingerprint: string }\n | { searchTargetId?: never; expectedMasterFingerprint?: never };\n\nconst DEFAULT_TIMEOUT_MS = 100_000; // SDK's own raw default is 15s (\"may need users' action on the device\") — too short for real confirmation.\n\nconst KEYSTONE_PUBLIC_DATA_UR_TYPE = 'qr-hardware-call';\n\nfunction toUrEncoded(ur: KeystoneUr): string {\n return new UREncoder(new UR(Buffer.from(ur.urData, 'hex'), ur.urType), Infinity)\n .nextPart()\n .toUpperCase();\n}\n\nfunction fromUrEncoded(encoded: unknown): KeystoneUr {\n // Accept either one complete UR or multiple whitespace-separated parts.\n // The transport owns EAPDU framing; this function only validates and joins\n // the BC-UR text present in that response.\n const text = typeof encoded === 'string' ? encoded.trim() : '';\n const parts = text.split(/\\s+/).filter(Boolean);\n const decoder = new URDecoder();\n for (const part of parts) {\n decoder.receivePart(part);\n if (decoder.isComplete()) break;\n }\n if (!parts.length || !decoder.isComplete() || !decoder.isSuccess()) {\n const shape =\n typeof encoded === 'string'\n ? `string len=${text.length} parts=${parts.length}`\n : typeof encoded;\n throw createHwkError({\n code: HardwareErrorCode.PayloadTooLarge,\n message: `Keystone USB returned an incomplete BC-UR response (${shape})`,\n origin: 'device',\n });\n }\n const ur = decoder.resultUR();\n return { urType: ur.type, urData: ur.cbor.toString('hex') };\n}\n\n/**\n * `IConnector` for Keystone over USB. Each transport call is its own\n * open/claim/transfer/close cycle, so `connect()` only caches the transport\n * object. `resolveUr` carries the same UR payloads as the QR channel.\n */\nexport class KeystoneUsbConnectorBase implements IConnector {\n readonly connectionType = 'usb' as const;\n\n private readonly transportClass: KeystoneUsbTransportStatic;\n\n private readonly timeoutMs: number;\n\n private readonly emitter = new TypedEventEmitter<ConnectorEventMap>();\n\n private readonly sessions = new Map<string, UsbSession>();\n\n private readonly discoveredDevices = new Map<string, KeystoneUsbDeviceDescriptor>();\n\n private readonly availabilityDevices = new Map<string, KeystoneUsbDeviceDescriptor>();\n\n private activeDiscoveryGeneration: symbol | undefined;\n\n private activeAvailabilityGeneration: symbol | undefined;\n\n // Keystone may re-enumerate on the USB bus between public-data exports,\n // which replaces the physical transport session without starting a new\n // user operation. Keep this UI-only state at connector lifetime scope so\n // an internal recovery does not look like another first connection.\n /** Wallets that already approved a public-data export, keyed by master fingerprint. */\n private readonly publicDataConfirmedWallets = new Set<string>();\n\n constructor(transportClass: KeystoneUsbTransportStatic, options?: { timeoutMs?: number }) {\n this.transportClass = transportClass;\n this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n private _invalidateDiscoverySnapshot(): void {\n this.activeDiscoveryGeneration = undefined;\n this.discoveredDevices.clear();\n }\n\n private _invalidateAvailabilitySnapshot(): void {\n this.activeAvailabilityGeneration = undefined;\n this.availabilityDevices.clear();\n }\n\n async searchDevices(options?: ConnectorSearchDevicesOptions): Promise<ConnectorDevice[]> {\n const isAvailabilitySearch = options?.purpose === 'availability';\n const discoveryGeneration = Symbol(\n isAvailabilitySearch ? 'keystone-usb-availability' : 'keystone-usb-discovery'\n );\n if (isAvailabilitySearch) {\n this._invalidateAvailabilitySnapshot();\n this.activeAvailabilityGeneration = discoveryGeneration;\n } else {\n this._invalidateDiscoverySnapshot();\n this.activeDiscoveryGeneration = discoveryGeneration;\n }\n // Enumeration throws DOMExceptions whose legacy `.code` collides with the\n // five-digit HWK range, so it needs the same mapping as every other\n // transport call here.\n let devices: ReadonlyArray<KeystoneUsbDeviceDescriptor>;\n try {\n devices = await this.transportClass.getKeystoneDevices();\n } catch (err) {\n throw mapKeystoneUsbError(err);\n }\n // No mfp is available without opening+claiming the device. The target id\n // therefore identifies only this discovery snapshot; wallet identity is\n // learned and verified after connect. WebUSB can reopen the exact cached\n // descriptor. Platforms without that ability fail closed for multi-device\n // selection instead of silently opening a different unit.\n if (\n (isAvailabilitySearch\n ? this.activeAvailabilityGeneration\n : this.activeDiscoveryGeneration) !== discoveryGeneration\n ) {\n return [];\n }\n return devices.map(device => {\n const connectId = createHardwareSearchTargetId('keystone');\n (isAvailabilitySearch ? this.availabilityDevices : this.discoveredDevices).set(\n connectId,\n device\n );\n return {\n connectId,\n deviceId: '',\n name: usbDisplayName(device),\n connectionType: 'usb',\n serialNumber: device.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n // Everything enumeration can see, verbatim. Nothing here is normalized\n // or defaulted: a missing key means the device published no such string\n // descriptor. Promote a field onto the typed surface above once\n // something actually consumes it.\n raw: {\n serialNumber: device.serialNumber,\n productName: device.productName,\n manufacturerName: device.manufacturerName,\n vendorId: device.vendorId,\n productId: device.productId,\n deviceVersionMajor: device.deviceVersionMajor,\n deviceVersionMinor: device.deviceVersionMinor,\n deviceVersionSubminor: device.deviceVersionSubminor,\n usbVersionMajor: device.usbVersionMajor,\n usbVersionMinor: device.usbVersionMinor,\n usbVersionSubminor: device.usbVersionSubminor,\n deviceClass: device.deviceClass,\n deviceSubclass: device.deviceSubclass,\n deviceProtocol: device.deviceProtocol,\n },\n };\n });\n }\n\n async connect(searchTargetIdOrExpectedMasterFingerprint?: string): Promise<ConnectorSession> {\n return this._connectResolved(\n this._resolveConnectInput(searchTargetIdOrExpectedMasterFingerprint)\n );\n }\n\n async connectTarget(target: ConnectorConnectTarget): Promise<ConnectorSession> {\n switch (target.type) {\n case 'default':\n return this._connectResolved({});\n case 'search-target':\n return this._connectResolved({ searchTargetId: target.searchTargetId });\n case 'expected-device-identity': {\n const expectedMasterFingerprint = parseBip32MasterFingerprint(target.deviceIdentity);\n if (!expectedMasterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector requires a valid master fingerprint identity',\n });\n }\n return this._connectResolved({ expectedMasterFingerprint });\n }\n default:\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Unsupported Keystone USB connection target',\n });\n }\n }\n\n private async _connectResolved({\n searchTargetId,\n expectedMasterFingerprint,\n }: KeystoneUsbConnectInput): Promise<ConnectorSession> {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.Searching,\n payload: { sessionId: '' },\n });\n let selectedDevice = searchTargetId\n ? this.discoveredDevices.get(searchTargetId) ?? this.availabilityDevices.get(searchTargetId)\n : undefined;\n if (searchTargetId && !selectedDevice) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: `Keystone USB search target is not in the current scan: ${searchTargetId}`,\n recovery: { scope: 'search-target' },\n });\n }\n let openedTransport: OpenedUsbTransport | undefined;\n try {\n let config: Awaited<ReturnType<KeystoneUsbConnectorBase['_readAppConfig']>>;\n\n if (expectedMasterFingerprint && !searchTargetId) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (!this.transportClass.connectDevice && availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'This platform cannot identify one of multiple connected Keystone devices',\n });\n }\n\n if (this.transportClass.connectDevice) {\n let readableDeviceFound = false;\n let lastError: unknown;\n let matched:\n | {\n device: KeystoneUsbDeviceDescriptor;\n opened: OpenedUsbTransport;\n config: Awaited<ReturnType<KeystoneUsbConnectorBase['_readAppConfig']>>;\n }\n | undefined;\n for (const device of availableDevices) {\n let candidate: OpenedUsbTransport | undefined;\n try {\n // eslint-disable-next-line no-await-in-loop\n candidate = await this._openTransport(device);\n // eslint-disable-next-line no-await-in-loop\n const candidateConfig = await this._readAppConfig(candidate.transport);\n readableDeviceFound = true;\n if (candidateConfig.mfp?.toLowerCase() === expectedMasterFingerprint.toLowerCase()) {\n matched = { device, opened: candidate, config: candidateConfig };\n break;\n }\n candidate.dispose();\n } catch (error) {\n candidate?.dispose();\n lastError = error;\n }\n }\n if (!matched) {\n if (!readableDeviceFound && lastError) throw mapKeystoneUsbError(lastError);\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message: `No connected Keystone wallet matches fingerprint ${expectedMasterFingerprint}`,\n });\n }\n selectedDevice = matched.device;\n openedTransport = matched.opened;\n config = matched.config;\n } else {\n openedTransport = await this._openTransport();\n config = await this._readAppConfig(openedTransport.transport);\n }\n } else {\n if (!selectedDevice && this.transportClass.connectDevice) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (availableDevices.length === 0) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: 'No Keystone USB device is available',\n });\n }\n if (availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'Select a Keystone USB device before connecting',\n });\n }\n [selectedDevice] = availableDevices;\n }\n if (selectedDevice && !this.transportClass.connectDevice) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'This platform cannot target one of multiple connected Keystone devices',\n });\n }\n }\n openedTransport = await this._openTransport(selectedDevice);\n config = await this._readAppConfig(openedTransport.transport);\n }\n\n if (!openedTransport) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: 'Keystone USB transport was not opened',\n });\n }\n const activeOpenedTransport = openedTransport;\n const { transport } = activeOpenedTransport;\n if (\n expectedMasterFingerprint &&\n config.mfp &&\n config.mfp.toLowerCase() !== expectedMasterFingerprint.toLowerCase()\n ) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message: `Connected Keystone wallet (mfp ${config.mfp}) does not match the requested wallet fingerprint (${expectedMasterFingerprint})`,\n });\n }\n const sessionId = createHardwareLinkId('keystone');\n activeOpenedTransport.bindSessionId(sessionId);\n this.sessions.set(sessionId, {\n transport,\n mfp: config.mfp,\n dispose: () => activeOpenedTransport.dispose(),\n });\n openedTransport = undefined;\n const device: ConnectorDevice = {\n connectId: sessionId,\n deviceId: '',\n name: selectedDevice ? usbDisplayName(selectedDevice) : 'Keystone',\n model: selectedDevice?.productName,\n connectionType: 'usb',\n serialNumber: selectedDevice?.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n raw: { masterFingerprint: config.mfp },\n };\n this.emitter.emit('device-connect', { device });\n return {\n sessionId,\n deviceInfo: this._toDeviceInfo(device, config.version, config.mfp),\n };\n } catch (err) {\n openedTransport?.dispose();\n throw mapKeystoneUsbError(err);\n }\n }\n\n private async _openTransport(device?: KeystoneUsbDeviceDescriptor): Promise<OpenedUsbTransport> {\n // A rejected candidate can keep its USB disconnect listener. Give every\n // descriptor a separate holder so it cannot retire a later matched session.\n let connectedSessionId: string | undefined;\n const transportConfig: TransportConfig = {\n timeout: this.timeoutMs,\n disconnectListener: () =>\n connectedSessionId ? this.disconnect(connectedSessionId) : undefined,\n };\n const transport = await (device && this.transportClass.connectDevice\n ? this.transportClass.connectDevice(device, transportConfig)\n : this.transportClass.connect(transportConfig));\n let disposed = false;\n return {\n transport,\n bindSessionId(sessionId: string) {\n connectedSessionId = sessionId;\n },\n dispose: () => {\n if (disposed) return;\n disposed = true;\n this.transportClass.disposeTransport?.(transport);\n },\n };\n }\n\n private _resolveConnectInput(value?: string): KeystoneUsbConnectInput {\n if (!value) return {};\n\n const runtimeId = parseHardwareRuntimeId(value);\n if (runtimeId) {\n // Whether this particular target is ours is settled by the snapshot\n // lookup in `_connectResolved`, not by anything encoded in the id.\n if (runtimeId.kind === 'search-target' && runtimeId.vendor === 'keystone') {\n return { searchTargetId: value };\n }\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector received an incompatible hardware runtime id',\n params: { kind: runtimeId.kind, vendor: runtimeId.vendor },\n });\n }\n\n if (hasHardwareRuntimeIdPrefix(value)) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector received a malformed hardware runtime id',\n });\n }\n\n const expectedMasterFingerprint = parseBip32MasterFingerprint(value);\n if (!expectedMasterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector requires a search target or master fingerprint',\n });\n }\n return { expectedMasterFingerprint };\n }\n\n disconnect(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n this.sessions.delete(sessionId);\n if (session) {\n session.dispose();\n // No persistent claim to release (see class doc) — nothing to await\n // here beyond letting the underlying transport's own per-call close\n // run its course, which already happened on the last send().\n this.emitter.emit('device-disconnect', { connectId: sessionId });\n }\n return Promise.resolve();\n }\n\n /** Keep the vendor transport as the EAPDU framing authority. */\n private async _sendResolveUr(transport: TransportHID, encodedUr: string): Promise<KeystoneUr> {\n const response = await transport.send<{ payload: string }>(Actions.CMD_RESOLVE_UR, encodedUr);\n return fromUrEncoded(response.payload);\n }\n\n async call(sessionId: string, method: string, params: unknown): Promise<ConnectorCallResult> {\n const session = this.sessions.get(sessionId);\n if (!session) {\n return {\n success: false,\n error: serializeConnectorError(\n createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: `Unknown Keystone USB session: ${sessionId}`,\n })\n ),\n };\n }\n\n try {\n switch (method) {\n case 'resolveUr': {\n const { urType, urData } = params as KeystoneUr;\n const encoded = toUrEncoded({ urType, urData });\n const isPublicDataRequest = urType === KEYSTONE_PUBLIC_DATA_UR_TYPE;\n // Public-data export asks for approval once per wallet: internal USB\n // re-enumeration must not reopen the toast, but a different device\n // has not approved anything yet. Signing always confirms.\n const publicDataKey = session.mfp?.toLowerCase() ?? sessionId;\n const shouldShowConfirmation =\n !isPublicDataRequest || !this.publicDataConfirmedWallets.has(publicDataKey);\n if (shouldShowConfirmation) {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.ConfirmOnDevice,\n payload: { sessionId },\n });\n }\n try {\n const response = await this._sendResolveUr(session.transport, encoded);\n if (isPublicDataRequest) {\n this.publicDataConfirmedWallets.add(publicDataKey);\n }\n return success(response);\n } finally {\n if (shouldShowConfirmation) {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.InteractionComplete,\n payload: { sessionId },\n });\n }\n }\n }\n case 'checkLockStatus': {\n const response = await session.transport.send<{ payload: boolean }>(\n Actions.CMD_CHECK_LOCK_STATUS,\n ''\n );\n // Naming-only assumption (no positive-case fixture to confirm\n // against) — `checkDeviceLockStatus`'s own name is the only\n // evidence `true` means locked; verify against real hardware.\n return success({ locked: response.payload });\n }\n case 'getAppConfig': {\n const config = await this._readAppConfig(session.transport);\n return success(config);\n }\n default:\n return {\n success: false,\n error: serializeConnectorError(\n createHwkError({\n code: HardwareErrorCode.MethodNotSupported,\n message: `Unknown Keystone USB connector method: ${method}`,\n })\n ),\n };\n }\n } catch (err) {\n return { success: false, error: serializeConnectorError(mapKeystoneUsbError(err)) };\n }\n }\n\n // No protocol-level cancel exists over USB (verified: nothing in the SDK's\n // public surface can interrupt an in-flight transferIn/transferOut) — the\n // best this connector can do is stop waiting on its side; the underlying\n // `send()` promise still settles (or times out) on its own.\n cancel(_sessionId: string): Promise<void> {\n return Promise.resolve();\n }\n\n // Keystone USB never relays PIN/passphrase through the host — entry always\n // happens on the device's own touchscreen — so there is nothing to answer.\n uiResponse(_response: UiResponseEvent): void {}\n\n on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void {\n this.emitter.on(event, handler);\n }\n\n off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void {\n this.emitter.off(event, handler);\n }\n\n reset(): void {\n for (const session of this.sessions.values()) {\n session.dispose();\n }\n this.sessions.clear();\n this._invalidateDiscoverySnapshot();\n this._invalidateAvailabilitySnapshot();\n this.publicDataConfirmedWallets.clear();\n }\n\n private async _readAppConfig(\n transport: TransportHID\n ): Promise<{ version?: string; mfp?: string }> {\n const response = await transport.send<Record<string, unknown>>(\n Actions.CMD_GET_DEVICE_VERSION,\n ''\n );\n const rawMasterFingerprint = response.walletMFP;\n const masterFingerprint = parseBip32MasterFingerprint(rawMasterFingerprint);\n if (rawMasterFingerprint !== undefined && !masterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message:\n 'Keystone returned an invalid master fingerprint; expected exactly 4 bytes (8 hex characters)',\n });\n }\n return {\n version: typeof response.firmwareVersion === 'string' ? response.firmwareVersion : undefined,\n mfp: masterFingerprint,\n };\n }\n\n private _toDeviceInfo(\n device: ConnectorDevice,\n firmwareVersion?: string,\n masterFingerprint?: string\n ) {\n return {\n vendor: 'keystone' as const,\n // The device publishes its model as a USB string descriptor\n // (`productName`, e.g. \"Keystone 3 Pro\"); `device.model` carries it here.\n // Empty rather than a literal 'unknown' when it says nothing, so the\n // host's own default-name fallback can win instead of a fake model\n // reaching the UI and the persisted device settings.\n model: device.model ?? '',\n modelName: device.modelName,\n firmwareVersion: firmwareVersion ?? '0.0.0',\n deviceId: device.deviceId,\n connectId: device.connectId,\n connectionType: 'usb' as const,\n serialNumber: device.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n raw: { masterFingerprint },\n };\n }\n}\n"],"mappings":"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","names":["HardwareErrorCode","createHwkError","createHwkError","HardwareErrorCode","device"]}
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
3
 
4
- var _chunkDMCM7F7Vjs = require('./chunk-DMCM7F7V.js');
4
+ var _chunkK6NZTBAUjs = require('./chunk-K6NZTBAU.js');
5
5
 
6
6
 
7
7
 
8
- exports.KeystoneUsbConnectorBase = _chunkDMCM7F7Vjs.KeystoneUsbConnectorBase; exports.mapKeystoneUsbError = _chunkDMCM7F7Vjs.mapKeystoneUsbError;
8
+ exports.KeystoneUsbConnectorBase = _chunkK6NZTBAUjs.KeystoneUsbConnectorBase; exports.mapKeystoneUsbError = _chunkK6NZTBAUjs.mapKeystoneUsbError;
9
9
  //# sourceMappingURL=index.js.map
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  KeystoneUsbConnectorBase,
3
3
  mapKeystoneUsbError
4
- } from "./chunk-2B6OTW7M.mjs";
4
+ } from "./chunk-U7YVTDVR.mjs";
5
5
  export {
6
6
  KeystoneUsbConnectorBase,
7
7
  mapKeystoneUsbError
package/dist/nodeusb.js CHANGED
@@ -1,11 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkDMCM7F7Vjs = require('./chunk-DMCM7F7V.js');
3
+ var _chunkK6NZTBAUjs = require('./chunk-K6NZTBAU.js');
4
4
 
5
5
  // src/_subpath/nodeusb.ts
6
6
  var _hwtransportnodeusb = require('@keystonehq/hw-transport-nodeusb');
7
7
  function createKeystoneNodeUsbConnector(options) {
8
- return new (0, _chunkDMCM7F7Vjs.KeystoneUsbConnectorBase)(_hwtransportnodeusb.TransportNodeUSB, options);
8
+ return new (0, _chunkK6NZTBAUjs.KeystoneUsbConnectorBase)(_hwtransportnodeusb.TransportNodeUSB, options);
9
9
  }
10
10
 
11
11
 
package/dist/nodeusb.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  KeystoneUsbConnectorBase
3
- } from "./chunk-2B6OTW7M.mjs";
3
+ } from "./chunk-U7YVTDVR.mjs";
4
4
 
5
5
  // src/_subpath/nodeusb.ts
6
6
  import { TransportNodeUSB } from "@keystonehq/hw-transport-nodeusb";
package/dist/webusb.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkDMCM7F7Vjs = require('./chunk-DMCM7F7V.js');
3
+ var _chunkK6NZTBAUjs = require('./chunk-K6NZTBAU.js');
4
4
 
5
5
  // src/_subpath/webusb.ts
6
6
  var _hwtransportwebusb = require('@keystonehq/hw-transport-webusb');
@@ -25,7 +25,7 @@ function createKeystoneWebUsbConnector(options) {
25
25
  disconnectListenerCleanupByTransport.set(transport, cleanup);
26
26
  return transport;
27
27
  };
28
- return new (0, _chunkDMCM7F7Vjs.KeystoneUsbConnectorBase)(
28
+ return new (0, _chunkK6NZTBAUjs.KeystoneUsbConnectorBase)(
29
29
  {
30
30
  connect: async (config) => {
31
31
  await _hwtransportwebusb.TransportWebUSB.isSupported();
package/dist/webusb.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  KeystoneUsbConnectorBase
3
- } from "./chunk-2B6OTW7M.mjs";
3
+ } from "./chunk-U7YVTDVR.mjs";
4
4
 
5
5
  // src/_subpath/webusb.ts
6
6
  import { TransportWebUSB } from "@keystonehq/hw-transport-webusb";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hwk-keystone-connector-usb",
3
- "version": "1.2.3-alpha.2",
3
+ "version": "1.2.3-alpha.4",
4
4
  "description": "IConnector implementation for Keystone hardware wallets via USB (WebUSB + Node USB)",
5
5
  "author": "OneKey",
6
6
  "license": "MIT",
@@ -69,7 +69,7 @@
69
69
  "@keystonehq/hw-transport-usb": "0.2.0",
70
70
  "@keystonehq/hw-transport-webusb": "0.5.3",
71
71
  "@ngraveio/bc-ur": "1.1.13",
72
- "@onekeyfe/hwk-adapter-core": "1.2.3-alpha.2"
72
+ "@onekeyfe/hwk-adapter-core": "1.2.3-alpha.4"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@keystonehq/bc-ur-registry-eth": "0.22.1",
@@ -86,5 +86,5 @@
86
86
  "optional": true
87
87
  }
88
88
  },
89
- "gitHead": "8b790c78394debdd3d9999db2f6a56235efb7a18"
89
+ "gitHead": "705729baaec54b86a4fc28cce310a85b67b82730"
90
90
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/KeystoneUsbConnectorBase.ts"],"sourcesContent":["import { HardwareErrorCode, createHwkError } from '@onekeyfe/hwk-adapter-core';\nimport { Status } from '@keystonehq/hw-transport-error';\n\nimport type { HwkError, HwkErrorOrigin } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Device-side rejections (`Status` values reported inside a JSON response\n * payload's status word) — see EAPDU_Readme.md in the Keystone USB SDK repo.\n */\nconst DEVICE_STATUS_MAP: Partial<Record<number, HardwareErrorCode>> = {\n [Status.PRS_PARSING_REJECTED]: HardwareErrorCode.UserRejected,\n [Status.PRS_PARSING_DISALLOWED]: HardwareErrorCode.DeviceLocked,\n [Status.PRS_PARSING_MISMATCHED_WALLET]: HardwareErrorCode.DeviceMismatch,\n [Status.PRS_EXPORT_ADDRESS_REJECTED]: HardwareErrorCode.UserRejected,\n [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: HardwareErrorCode.DeviceLocked,\n};\n\n/** Client-side (transport/framing) failures — never reached the device. */\nconst CLIENT_STATUS_MAP: Partial<Record<number, HardwareErrorCode>> = {\n [Status.ERR_DEVICE_NOT_OPENED]: HardwareErrorCode.DeviceNotFound,\n [Status.ERR_DEVICE_NOT_FOUND]: HardwareErrorCode.DeviceNotFound,\n [Status.ERR_TIMEOUT]: HardwareErrorCode.OperationTimeout,\n [Status.ERR_DATA_TOO_LARGE]: HardwareErrorCode.PayloadTooLarge,\n [Status.ERR_NOT_SUPPORTED]: HardwareErrorCode.TransportNotAvailable,\n};\n\n/**\n * Maps a Keystone USB SDK failure to `HardwareErrorCode`. Errors that already\n * carry a numeric `.code` pass through unchanged. `transportErrorCode` is read\n * duck-typed because duplicate `@keystonehq/*` module instances break\n * `instanceof`.\n */\nexport function mapKeystoneUsbError(err: unknown): HwkError {\n const domName =\n err && typeof err === 'object' && typeof (err as { name?: unknown }).name === 'string'\n ? (err as { name: string }).name\n : undefined;\n\n // Legacy DOMException codes overlap the low-numbered browser error table\n // (for example NotFoundError is 8). Only five-digit HWK codes are already\n // mapped errors; letting any numeric `.code` pass through turns a WebUSB\n // disconnect into an unknown hardware error at the app boundary.\n if (\n err &&\n typeof err === 'object' &&\n typeof (err as { code?: unknown }).code === 'number' &&\n (err as { code: number }).code >= 10000 &&\n (err as { code: number }).code <= 99999\n ) {\n return err as HwkError;\n }\n\n const statusCode =\n err &&\n typeof err === 'object' &&\n typeof (err as { transportErrorCode?: unknown }).transportErrorCode === 'number'\n ? (err as { transportErrorCode: number }).transportErrorCode\n : undefined;\n\n const message = err instanceof Error ? err.message : String(err);\n\n if (statusCode !== undefined) {\n const deviceCode = DEVICE_STATUS_MAP[statusCode];\n const code = deviceCode ?? CLIENT_STATUS_MAP[statusCode] ?? HardwareErrorCode.TransportError;\n let origin: HwkErrorOrigin | undefined;\n if (deviceCode !== undefined) {\n origin = 'device';\n } else if (code !== HardwareErrorCode.OperationTimeout) {\n origin = 'transport';\n }\n return createHwkError({\n code,\n message,\n // A DEVICE_STATUS_MAP hit is the firmware answering (rejection, locked,\n // mismatched wallet) — a result, never a link problem. Client-side\n // status codes came from the framing/pipe layer — EXCEPT the timeout,\n // which is genuinely two-faced (the device may be sitting on a confirm\n // screen waiting for a human, or the pipe may be dead) and stays\n // unlabeled rather than mislabeled.\n origin,\n params: { statusCode, details: (err as { details?: string }).details },\n });\n }\n\n // WebUSB failures are DOMExceptions. The device dropping off the bus\n // mid-call is the common one — every command here is its own\n // open/claim/transfer/release/close cycle, so an unplug or a bus reset lands\n // exactly here — and it deserves a message the user can act on.\n if (domName === 'NotFoundError' || domName === 'NetworkError') {\n return createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message,\n origin: 'transport',\n params: { domExceptionName: domName },\n });\n }\n if (domName === 'SecurityError' || domName === 'NotAllowedError') {\n return createHwkError({\n code: HardwareErrorCode.DevicePermissionDenied,\n message,\n origin: 'host',\n params: { domExceptionName: domName },\n });\n }\n if (domName === 'InvalidStateError' || domName === 'AbortError') {\n return createHwkError({\n code: HardwareErrorCode.DeviceBusy,\n message,\n origin: 'transport',\n params: { domExceptionName: domName },\n });\n }\n\n // Anything else that reached this mapper was thrown by the transport layer\n // (this function only wraps USB transport calls), so 'transport' is a fact\n // about the throw site, not a guess about the cause.\n return createHwkError({ code: HardwareErrorCode.TransportError, message, origin: 'transport' });\n}\n","import { UR, URDecoder, UREncoder } from '@ngraveio/bc-ur';\nimport { Actions } from '@keystonehq/hw-transport-usb';\nimport {\n EConnectorInteraction,\n HardwareErrorCode,\n TypedEventEmitter,\n createHardwareLinkId,\n createHardwareSearchTargetId,\n createHwkError,\n hasHardwareRuntimeIdPrefix,\n parseBip32MasterFingerprint,\n parseHardwareRuntimeId,\n serializeConnectorError,\n success,\n} from '@onekeyfe/hwk-adapter-core';\n\nimport { mapKeystoneUsbError } from './errors';\n\nimport type {\n ConnectorCallResult,\n ConnectorConnectTarget,\n ConnectorDevice,\n ConnectorEventMap,\n ConnectorEventType,\n ConnectorSearchDevicesOptions,\n ConnectorSession,\n IConnector,\n UiResponseEvent,\n} from '@onekeyfe/hwk-adapter-core';\nimport type { TransportConfig, TransportHID } from '@keystonehq/hw-transport-usb';\n\n/**\n * Static surface shared by `TransportWebUSB` and `TransportNodeUSB`; the\n * platform-specific transport is injected by `_subpath/*`.\n */\n/**\n * What enumeration can see before the device is opened. WebUSB hands back a\n * `USBDevice`; node-usb's wrapper exposes the same descriptor strings. Every\n * field is optional because a device may simply not publish that string\n * descriptor — read defensively, never assume presence.\n */\nexport interface KeystoneUsbDeviceDescriptor {\n serialNumber?: string;\n productName?: string;\n manufacturerName?: string;\n vendorId?: number;\n productId?: number;\n /** Device release number (bcdDevice), split into its three nibble groups. */\n deviceVersionMajor?: number;\n deviceVersionMinor?: number;\n deviceVersionSubminor?: number;\n usbVersionMajor?: number;\n usbVersionMinor?: number;\n usbVersionSubminor?: number;\n deviceClass?: number;\n deviceSubclass?: number;\n deviceProtocol?: number;\n}\n\n/**\n * Enumeration-time display name. Every Keystone shares one vid/pid and reports\n * the same `productName`, so the name alone cannot tell two attached units\n * apart — the serial's suffix is the only discriminator available before the\n * device is opened. `M-76AB5599` -> `Keystone 76AB5599`.\n */\nfunction usbDisplayName(device: KeystoneUsbDeviceDescriptor): string {\n const base = device.productName?.trim() || 'Keystone';\n const suffix = device.serialNumber?.split('-').pop()?.trim();\n return suffix ? `${base} ${suffix}` : base;\n}\n\nexport interface KeystoneUsbTransportStatic {\n connect(config?: TransportConfig): Promise<TransportHID>;\n connectDevice?(\n device: KeystoneUsbDeviceDescriptor,\n config?: TransportConfig\n ): Promise<TransportHID>;\n getKeystoneDevices(): Promise<ReadonlyArray<KeystoneUsbDeviceDescriptor>>;\n requestPermission?(): Promise<void>;\n isSupported(): Promise<boolean>;\n /** Release transport-lifetime resources owned by the platform wrapper. */\n disposeTransport?(transport: TransportHID): void;\n}\n\ninterface KeystoneUr {\n urType: string;\n urData: string;\n}\n\ninterface UsbSession {\n transport: TransportHID;\n mfp?: string;\n dispose(): void;\n}\n\ninterface OpenedUsbTransport {\n transport: TransportHID;\n bindSessionId(sessionId: string): void;\n dispose(): void;\n}\n\ntype KeystoneUsbConnectInput =\n | { searchTargetId: string; expectedMasterFingerprint?: never }\n | { searchTargetId?: never; expectedMasterFingerprint: string }\n | { searchTargetId?: never; expectedMasterFingerprint?: never };\n\nconst DEFAULT_TIMEOUT_MS = 100_000; // SDK's own raw default is 15s (\"may need users' action on the device\") — too short for real confirmation.\n\nconst KEYSTONE_PUBLIC_DATA_UR_TYPE = 'qr-hardware-call';\n\nfunction toUrEncoded(ur: KeystoneUr): string {\n return new UREncoder(new UR(Buffer.from(ur.urData, 'hex'), ur.urType), Infinity)\n .nextPart()\n .toUpperCase();\n}\n\nfunction fromUrEncoded(encoded: unknown): KeystoneUr {\n // Accept either one complete UR or multiple whitespace-separated parts.\n // The transport owns EAPDU framing; this function only validates and joins\n // the BC-UR text present in that response.\n const text = typeof encoded === 'string' ? encoded.trim() : '';\n const parts = text.split(/\\s+/).filter(Boolean);\n const decoder = new URDecoder();\n for (const part of parts) {\n decoder.receivePart(part);\n if (decoder.isComplete()) break;\n }\n if (!parts.length || !decoder.isComplete() || !decoder.isSuccess()) {\n const shape =\n typeof encoded === 'string'\n ? `string len=${text.length} parts=${parts.length}`\n : typeof encoded;\n throw createHwkError({\n code: HardwareErrorCode.PayloadTooLarge,\n message: `Keystone USB returned an incomplete BC-UR response (${shape})`,\n origin: 'device',\n });\n }\n const ur = decoder.resultUR();\n return { urType: ur.type, urData: ur.cbor.toString('hex') };\n}\n\n/**\n * `IConnector` for Keystone over USB. Each transport call is its own\n * open/claim/transfer/close cycle, so `connect()` only caches the transport\n * object. `resolveUr` carries the same UR payloads as the QR channel.\n */\nexport class KeystoneUsbConnectorBase implements IConnector {\n readonly connectionType = 'usb' as const;\n\n private readonly transportClass: KeystoneUsbTransportStatic;\n\n private readonly timeoutMs: number;\n\n private readonly emitter = new TypedEventEmitter<ConnectorEventMap>();\n\n private readonly sessions = new Map<string, UsbSession>();\n\n private readonly discoveredDevices = new Map<string, KeystoneUsbDeviceDescriptor>();\n\n private readonly availabilityDevices = new Map<string, KeystoneUsbDeviceDescriptor>();\n\n private activeDiscoveryGeneration: symbol | undefined;\n\n private activeAvailabilityGeneration: symbol | undefined;\n\n // Keystone may re-enumerate on the USB bus between public-data exports,\n // which replaces the physical transport session without starting a new\n // user operation. Keep this UI-only state at connector lifetime scope so\n // an internal recovery does not look like another first connection.\n /** Wallets that already approved a public-data export, keyed by master fingerprint. */\n private readonly publicDataConfirmedWallets = new Set<string>();\n\n constructor(transportClass: KeystoneUsbTransportStatic, options?: { timeoutMs?: number }) {\n this.transportClass = transportClass;\n this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n private _invalidateDiscoverySnapshot(): void {\n this.activeDiscoveryGeneration = undefined;\n this.discoveredDevices.clear();\n }\n\n private _invalidateAvailabilitySnapshot(): void {\n this.activeAvailabilityGeneration = undefined;\n this.availabilityDevices.clear();\n }\n\n async searchDevices(options?: ConnectorSearchDevicesOptions): Promise<ConnectorDevice[]> {\n const isAvailabilitySearch = options?.purpose === 'availability';\n const discoveryGeneration = Symbol(\n isAvailabilitySearch ? 'keystone-usb-availability' : 'keystone-usb-discovery'\n );\n if (isAvailabilitySearch) {\n this._invalidateAvailabilitySnapshot();\n this.activeAvailabilityGeneration = discoveryGeneration;\n } else {\n this._invalidateDiscoverySnapshot();\n this.activeDiscoveryGeneration = discoveryGeneration;\n }\n const devices = await this.transportClass.getKeystoneDevices();\n // No mfp is available without opening+claiming the device. The target id\n // therefore identifies only this discovery snapshot; wallet identity is\n // learned and verified after connect. WebUSB can reopen the exact cached\n // descriptor. Platforms without that ability fail closed for multi-device\n // selection instead of silently opening a different unit.\n if (\n (isAvailabilitySearch\n ? this.activeAvailabilityGeneration\n : this.activeDiscoveryGeneration) !== discoveryGeneration\n ) {\n return [];\n }\n return devices.map(device => {\n const connectId = createHardwareSearchTargetId('keystone');\n (isAvailabilitySearch ? this.availabilityDevices : this.discoveredDevices).set(\n connectId,\n device\n );\n return {\n connectId,\n deviceId: '',\n name: usbDisplayName(device),\n connectionType: 'usb',\n serialNumber: device.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n // Everything enumeration can see, verbatim. Nothing here is normalized\n // or defaulted: a missing key means the device published no such string\n // descriptor. Promote a field onto the typed surface above once\n // something actually consumes it.\n raw: {\n serialNumber: device.serialNumber,\n productName: device.productName,\n manufacturerName: device.manufacturerName,\n vendorId: device.vendorId,\n productId: device.productId,\n deviceVersionMajor: device.deviceVersionMajor,\n deviceVersionMinor: device.deviceVersionMinor,\n deviceVersionSubminor: device.deviceVersionSubminor,\n usbVersionMajor: device.usbVersionMajor,\n usbVersionMinor: device.usbVersionMinor,\n usbVersionSubminor: device.usbVersionSubminor,\n deviceClass: device.deviceClass,\n deviceSubclass: device.deviceSubclass,\n deviceProtocol: device.deviceProtocol,\n },\n };\n });\n }\n\n async connect(searchTargetIdOrExpectedMasterFingerprint?: string): Promise<ConnectorSession> {\n return this._connectResolved(\n this._resolveConnectInput(searchTargetIdOrExpectedMasterFingerprint)\n );\n }\n\n async connectTarget(target: ConnectorConnectTarget): Promise<ConnectorSession> {\n switch (target.type) {\n case 'default':\n return this._connectResolved({});\n case 'search-target':\n return this._connectResolved({ searchTargetId: target.searchTargetId });\n case 'expected-device-identity': {\n const expectedMasterFingerprint = parseBip32MasterFingerprint(target.deviceIdentity);\n if (!expectedMasterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector requires a valid master fingerprint identity',\n });\n }\n return this._connectResolved({ expectedMasterFingerprint });\n }\n default:\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Unsupported Keystone USB connection target',\n });\n }\n }\n\n private async _connectResolved({\n searchTargetId,\n expectedMasterFingerprint,\n }: KeystoneUsbConnectInput): Promise<ConnectorSession> {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.Searching,\n payload: { sessionId: '' },\n });\n let selectedDevice = searchTargetId\n ? this.discoveredDevices.get(searchTargetId) ?? this.availabilityDevices.get(searchTargetId)\n : undefined;\n if (searchTargetId && !selectedDevice) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: `Keystone USB search target is not in the current scan: ${searchTargetId}`,\n recovery: { scope: 'search-target' },\n });\n }\n let openedTransport: OpenedUsbTransport | undefined;\n try {\n let config: Awaited<ReturnType<KeystoneUsbConnectorBase['_readAppConfig']>>;\n\n if (expectedMasterFingerprint && !searchTargetId) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (!this.transportClass.connectDevice && availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'This platform cannot identify one of multiple connected Keystone devices',\n });\n }\n\n if (this.transportClass.connectDevice) {\n let readableDeviceFound = false;\n let lastError: unknown;\n let matched:\n | {\n device: KeystoneUsbDeviceDescriptor;\n opened: OpenedUsbTransport;\n config: Awaited<ReturnType<KeystoneUsbConnectorBase['_readAppConfig']>>;\n }\n | undefined;\n for (const device of availableDevices) {\n let candidate: OpenedUsbTransport | undefined;\n try {\n // eslint-disable-next-line no-await-in-loop\n candidate = await this._openTransport(device);\n // eslint-disable-next-line no-await-in-loop\n const candidateConfig = await this._readAppConfig(candidate.transport);\n readableDeviceFound = true;\n if (candidateConfig.mfp?.toLowerCase() === expectedMasterFingerprint.toLowerCase()) {\n matched = { device, opened: candidate, config: candidateConfig };\n break;\n }\n candidate.dispose();\n } catch (error) {\n candidate?.dispose();\n lastError = error;\n }\n }\n if (!matched) {\n if (!readableDeviceFound && lastError) throw mapKeystoneUsbError(lastError);\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message: `No connected Keystone wallet matches fingerprint ${expectedMasterFingerprint}`,\n });\n }\n selectedDevice = matched.device;\n openedTransport = matched.opened;\n config = matched.config;\n } else {\n openedTransport = await this._openTransport();\n config = await this._readAppConfig(openedTransport.transport);\n }\n } else {\n if (!selectedDevice && this.transportClass.connectDevice) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (availableDevices.length === 0) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: 'No Keystone USB device is available',\n });\n }\n if (availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'Select a Keystone USB device before connecting',\n });\n }\n [selectedDevice] = availableDevices;\n }\n if (selectedDevice && !this.transportClass.connectDevice) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'This platform cannot target one of multiple connected Keystone devices',\n });\n }\n }\n openedTransport = await this._openTransport(selectedDevice);\n config = await this._readAppConfig(openedTransport.transport);\n }\n\n if (!openedTransport) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: 'Keystone USB transport was not opened',\n });\n }\n const activeOpenedTransport = openedTransport;\n const { transport } = activeOpenedTransport;\n if (\n expectedMasterFingerprint &&\n config.mfp &&\n config.mfp.toLowerCase() !== expectedMasterFingerprint.toLowerCase()\n ) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message: `Connected Keystone wallet (mfp ${config.mfp}) does not match the requested wallet fingerprint (${expectedMasterFingerprint})`,\n });\n }\n const sessionId = createHardwareLinkId('keystone');\n activeOpenedTransport.bindSessionId(sessionId);\n this.sessions.set(sessionId, {\n transport,\n mfp: config.mfp,\n dispose: () => activeOpenedTransport.dispose(),\n });\n openedTransport = undefined;\n const device: ConnectorDevice = {\n connectId: sessionId,\n deviceId: '',\n name: selectedDevice ? usbDisplayName(selectedDevice) : 'Keystone',\n model: selectedDevice?.productName,\n connectionType: 'usb',\n serialNumber: selectedDevice?.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n raw: { masterFingerprint: config.mfp },\n };\n this.emitter.emit('device-connect', { device });\n return {\n sessionId,\n deviceInfo: this._toDeviceInfo(device, config.version, config.mfp),\n };\n } catch (err) {\n openedTransport?.dispose();\n throw mapKeystoneUsbError(err);\n }\n }\n\n private async _openTransport(device?: KeystoneUsbDeviceDescriptor): Promise<OpenedUsbTransport> {\n // A rejected candidate can keep its USB disconnect listener. Give every\n // descriptor a separate holder so it cannot retire a later matched session.\n let connectedSessionId: string | undefined;\n const transportConfig: TransportConfig = {\n timeout: this.timeoutMs,\n disconnectListener: () =>\n connectedSessionId ? this.disconnect(connectedSessionId) : undefined,\n };\n const transport = await (device && this.transportClass.connectDevice\n ? this.transportClass.connectDevice(device, transportConfig)\n : this.transportClass.connect(transportConfig));\n let disposed = false;\n return {\n transport,\n bindSessionId(sessionId: string) {\n connectedSessionId = sessionId;\n },\n dispose: () => {\n if (disposed) return;\n disposed = true;\n this.transportClass.disposeTransport?.(transport);\n },\n };\n }\n\n private _resolveConnectInput(value?: string): KeystoneUsbConnectInput {\n if (!value) return {};\n\n const runtimeId = parseHardwareRuntimeId(value);\n if (runtimeId) {\n // Whether this particular target is ours is settled by the snapshot\n // lookup in `_connectResolved`, not by anything encoded in the id.\n if (runtimeId.kind === 'search-target' && runtimeId.vendor === 'keystone') {\n return { searchTargetId: value };\n }\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector received an incompatible hardware runtime id',\n params: { kind: runtimeId.kind, vendor: runtimeId.vendor },\n });\n }\n\n if (hasHardwareRuntimeIdPrefix(value)) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector received a malformed hardware runtime id',\n });\n }\n\n const expectedMasterFingerprint = parseBip32MasterFingerprint(value);\n if (!expectedMasterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector requires a search target or master fingerprint',\n });\n }\n return { expectedMasterFingerprint };\n }\n\n disconnect(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n this.sessions.delete(sessionId);\n if (session) {\n session.dispose();\n // No persistent claim to release (see class doc) — nothing to await\n // here beyond letting the underlying transport's own per-call close\n // run its course, which already happened on the last send().\n this.emitter.emit('device-disconnect', { connectId: sessionId });\n }\n return Promise.resolve();\n }\n\n /** Keep the vendor transport as the EAPDU framing authority. */\n private async _sendResolveUr(transport: TransportHID, encodedUr: string): Promise<KeystoneUr> {\n const response = await transport.send<{ payload: string }>(Actions.CMD_RESOLVE_UR, encodedUr);\n return fromUrEncoded(response.payload);\n }\n\n async call(sessionId: string, method: string, params: unknown): Promise<ConnectorCallResult> {\n const session = this.sessions.get(sessionId);\n if (!session) {\n return {\n success: false,\n error: serializeConnectorError(\n createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: `Unknown Keystone USB session: ${sessionId}`,\n })\n ),\n };\n }\n\n try {\n switch (method) {\n case 'resolveUr': {\n const { urType, urData } = params as KeystoneUr;\n const encoded = toUrEncoded({ urType, urData });\n const isPublicDataRequest = urType === KEYSTONE_PUBLIC_DATA_UR_TYPE;\n // Public-data export asks for approval once per wallet: internal USB\n // re-enumeration must not reopen the toast, but a different device\n // has not approved anything yet. Signing always confirms.\n const publicDataKey = session.mfp?.toLowerCase() ?? sessionId;\n const shouldShowConfirmation =\n !isPublicDataRequest || !this.publicDataConfirmedWallets.has(publicDataKey);\n if (shouldShowConfirmation) {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.ConfirmOnDevice,\n payload: { sessionId },\n });\n }\n try {\n const response = await this._sendResolveUr(session.transport, encoded);\n if (isPublicDataRequest) {\n this.publicDataConfirmedWallets.add(publicDataKey);\n }\n return success(response);\n } finally {\n if (shouldShowConfirmation) {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.InteractionComplete,\n payload: { sessionId },\n });\n }\n }\n }\n case 'checkLockStatus': {\n const response = await session.transport.send<{ payload: boolean }>(\n Actions.CMD_CHECK_LOCK_STATUS,\n ''\n );\n // Naming-only assumption (no positive-case fixture to confirm\n // against) — `checkDeviceLockStatus`'s own name is the only\n // evidence `true` means locked; verify against real hardware.\n return success({ locked: response.payload });\n }\n case 'getAppConfig': {\n const config = await this._readAppConfig(session.transport);\n return success(config);\n }\n default:\n return {\n success: false,\n error: serializeConnectorError(\n createHwkError({\n code: HardwareErrorCode.MethodNotSupported,\n message: `Unknown Keystone USB connector method: ${method}`,\n })\n ),\n };\n }\n } catch (err) {\n return { success: false, error: serializeConnectorError(mapKeystoneUsbError(err)) };\n }\n }\n\n // No protocol-level cancel exists over USB (verified: nothing in the SDK's\n // public surface can interrupt an in-flight transferIn/transferOut) — the\n // best this connector can do is stop waiting on its side; the underlying\n // `send()` promise still settles (or times out) on its own.\n cancel(_sessionId: string): Promise<void> {\n return Promise.resolve();\n }\n\n // Keystone USB never relays PIN/passphrase through the host — entry always\n // happens on the device's own touchscreen — so there is nothing to answer.\n uiResponse(_response: UiResponseEvent): void {}\n\n on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void {\n this.emitter.on(event, handler);\n }\n\n off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void {\n this.emitter.off(event, handler);\n }\n\n reset(): void {\n for (const session of this.sessions.values()) {\n session.dispose();\n }\n this.sessions.clear();\n this._invalidateDiscoverySnapshot();\n this._invalidateAvailabilitySnapshot();\n this.publicDataConfirmedWallets.clear();\n }\n\n private async _readAppConfig(\n transport: TransportHID\n ): Promise<{ version?: string; mfp?: string }> {\n const response = await transport.send<Record<string, unknown>>(\n Actions.CMD_GET_DEVICE_VERSION,\n ''\n );\n const rawMasterFingerprint = response.walletMFP;\n const masterFingerprint = parseBip32MasterFingerprint(rawMasterFingerprint);\n if (rawMasterFingerprint !== undefined && !masterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message:\n 'Keystone returned an invalid master fingerprint; expected exactly 4 bytes (8 hex characters)',\n });\n }\n return {\n version: typeof response.firmwareVersion === 'string' ? response.firmwareVersion : undefined,\n mfp: masterFingerprint,\n };\n }\n\n private _toDeviceInfo(\n device: ConnectorDevice,\n firmwareVersion?: string,\n masterFingerprint?: string\n ) {\n return {\n vendor: 'keystone' as const,\n // The device publishes its model as a USB string descriptor\n // (`productName`, e.g. \"Keystone 3 Pro\"); `device.model` carries it here.\n // Empty rather than a literal 'unknown' when it says nothing, so the\n // host's own default-name fallback can win instead of a fake model\n // reaching the UI and the persisted device settings.\n model: device.model ?? '',\n modelName: device.modelName,\n firmwareVersion: firmwareVersion ?? '0.0.0',\n deviceId: device.deviceId,\n connectId: device.connectId,\n connectionType: 'usb' as const,\n serialNumber: device.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n raw: { masterFingerprint },\n };\n }\n}\n"],"mappings":"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","names":["HardwareErrorCode","createHwkError","createHwkError","HardwareErrorCode","device"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-keystone-connector-usb/dist/chunk-DMCM7F7V.js","../src/errors.ts","../src/KeystoneUsbConnectorBase.ts"],"names":["createHwkError","HardwareErrorCode"],"mappings":"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","file":"/home/runner/work/hardware-js-sdk/hardware-js-sdk/packages/hwk-keystone-connector-usb/dist/chunk-DMCM7F7V.js","sourcesContent":[null,"import { HardwareErrorCode, createHwkError } from '@onekeyfe/hwk-adapter-core';\nimport { Status } from '@keystonehq/hw-transport-error';\n\nimport type { HwkError, HwkErrorOrigin } from '@onekeyfe/hwk-adapter-core';\n\n/**\n * Device-side rejections (`Status` values reported inside a JSON response\n * payload's status word) — see EAPDU_Readme.md in the Keystone USB SDK repo.\n */\nconst DEVICE_STATUS_MAP: Partial<Record<number, HardwareErrorCode>> = {\n [Status.PRS_PARSING_REJECTED]: HardwareErrorCode.UserRejected,\n [Status.PRS_PARSING_DISALLOWED]: HardwareErrorCode.DeviceLocked,\n [Status.PRS_PARSING_MISMATCHED_WALLET]: HardwareErrorCode.DeviceMismatch,\n [Status.PRS_EXPORT_ADDRESS_REJECTED]: HardwareErrorCode.UserRejected,\n [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: HardwareErrorCode.DeviceLocked,\n};\n\n/** Client-side (transport/framing) failures — never reached the device. */\nconst CLIENT_STATUS_MAP: Partial<Record<number, HardwareErrorCode>> = {\n [Status.ERR_DEVICE_NOT_OPENED]: HardwareErrorCode.DeviceNotFound,\n [Status.ERR_DEVICE_NOT_FOUND]: HardwareErrorCode.DeviceNotFound,\n [Status.ERR_TIMEOUT]: HardwareErrorCode.OperationTimeout,\n [Status.ERR_DATA_TOO_LARGE]: HardwareErrorCode.PayloadTooLarge,\n [Status.ERR_NOT_SUPPORTED]: HardwareErrorCode.TransportNotAvailable,\n};\n\n/**\n * Maps a Keystone USB SDK failure to `HardwareErrorCode`. Errors that already\n * carry a numeric `.code` pass through unchanged. `transportErrorCode` is read\n * duck-typed because duplicate `@keystonehq/*` module instances break\n * `instanceof`.\n */\nexport function mapKeystoneUsbError(err: unknown): HwkError {\n const domName =\n err && typeof err === 'object' && typeof (err as { name?: unknown }).name === 'string'\n ? (err as { name: string }).name\n : undefined;\n\n // Legacy DOMException codes overlap the low-numbered browser error table\n // (for example NotFoundError is 8). Only five-digit HWK codes are already\n // mapped errors; letting any numeric `.code` pass through turns a WebUSB\n // disconnect into an unknown hardware error at the app boundary.\n if (\n err &&\n typeof err === 'object' &&\n typeof (err as { code?: unknown }).code === 'number' &&\n (err as { code: number }).code >= 10000 &&\n (err as { code: number }).code <= 99999\n ) {\n return err as HwkError;\n }\n\n const statusCode =\n err &&\n typeof err === 'object' &&\n typeof (err as { transportErrorCode?: unknown }).transportErrorCode === 'number'\n ? (err as { transportErrorCode: number }).transportErrorCode\n : undefined;\n\n const message = err instanceof Error ? err.message : String(err);\n\n if (statusCode !== undefined) {\n const deviceCode = DEVICE_STATUS_MAP[statusCode];\n const code = deviceCode ?? CLIENT_STATUS_MAP[statusCode] ?? HardwareErrorCode.TransportError;\n let origin: HwkErrorOrigin | undefined;\n if (deviceCode !== undefined) {\n origin = 'device';\n } else if (code !== HardwareErrorCode.OperationTimeout) {\n origin = 'transport';\n }\n return createHwkError({\n code,\n message,\n // A DEVICE_STATUS_MAP hit is the firmware answering (rejection, locked,\n // mismatched wallet) — a result, never a link problem. Client-side\n // status codes came from the framing/pipe layer — EXCEPT the timeout,\n // which is genuinely two-faced (the device may be sitting on a confirm\n // screen waiting for a human, or the pipe may be dead) and stays\n // unlabeled rather than mislabeled.\n origin,\n params: { statusCode, details: (err as { details?: string }).details },\n });\n }\n\n // WebUSB failures are DOMExceptions. The device dropping off the bus\n // mid-call is the common one — every command here is its own\n // open/claim/transfer/release/close cycle, so an unplug or a bus reset lands\n // exactly here — and it deserves a message the user can act on.\n if (domName === 'NotFoundError' || domName === 'NetworkError') {\n return createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message,\n origin: 'transport',\n params: { domExceptionName: domName },\n });\n }\n if (domName === 'SecurityError' || domName === 'NotAllowedError') {\n return createHwkError({\n code: HardwareErrorCode.DevicePermissionDenied,\n message,\n origin: 'host',\n params: { domExceptionName: domName },\n });\n }\n if (domName === 'InvalidStateError' || domName === 'AbortError') {\n return createHwkError({\n code: HardwareErrorCode.DeviceBusy,\n message,\n origin: 'transport',\n params: { domExceptionName: domName },\n });\n }\n\n // Anything else that reached this mapper was thrown by the transport layer\n // (this function only wraps USB transport calls), so 'transport' is a fact\n // about the throw site, not a guess about the cause.\n return createHwkError({ code: HardwareErrorCode.TransportError, message, origin: 'transport' });\n}\n","import { UR, URDecoder, UREncoder } from '@ngraveio/bc-ur';\nimport { Actions } from '@keystonehq/hw-transport-usb';\nimport {\n EConnectorInteraction,\n HardwareErrorCode,\n TypedEventEmitter,\n createHardwareLinkId,\n createHardwareSearchTargetId,\n createHwkError,\n hasHardwareRuntimeIdPrefix,\n parseBip32MasterFingerprint,\n parseHardwareRuntimeId,\n serializeConnectorError,\n success,\n} from '@onekeyfe/hwk-adapter-core';\n\nimport { mapKeystoneUsbError } from './errors';\n\nimport type {\n ConnectorCallResult,\n ConnectorConnectTarget,\n ConnectorDevice,\n ConnectorEventMap,\n ConnectorEventType,\n ConnectorSearchDevicesOptions,\n ConnectorSession,\n IConnector,\n UiResponseEvent,\n} from '@onekeyfe/hwk-adapter-core';\nimport type { TransportConfig, TransportHID } from '@keystonehq/hw-transport-usb';\n\n/**\n * Static surface shared by `TransportWebUSB` and `TransportNodeUSB`; the\n * platform-specific transport is injected by `_subpath/*`.\n */\n/**\n * What enumeration can see before the device is opened. WebUSB hands back a\n * `USBDevice`; node-usb's wrapper exposes the same descriptor strings. Every\n * field is optional because a device may simply not publish that string\n * descriptor — read defensively, never assume presence.\n */\nexport interface KeystoneUsbDeviceDescriptor {\n serialNumber?: string;\n productName?: string;\n manufacturerName?: string;\n vendorId?: number;\n productId?: number;\n /** Device release number (bcdDevice), split into its three nibble groups. */\n deviceVersionMajor?: number;\n deviceVersionMinor?: number;\n deviceVersionSubminor?: number;\n usbVersionMajor?: number;\n usbVersionMinor?: number;\n usbVersionSubminor?: number;\n deviceClass?: number;\n deviceSubclass?: number;\n deviceProtocol?: number;\n}\n\n/**\n * Enumeration-time display name. Every Keystone shares one vid/pid and reports\n * the same `productName`, so the name alone cannot tell two attached units\n * apart — the serial's suffix is the only discriminator available before the\n * device is opened. `M-76AB5599` -> `Keystone 76AB5599`.\n */\nfunction usbDisplayName(device: KeystoneUsbDeviceDescriptor): string {\n const base = device.productName?.trim() || 'Keystone';\n const suffix = device.serialNumber?.split('-').pop()?.trim();\n return suffix ? `${base} ${suffix}` : base;\n}\n\nexport interface KeystoneUsbTransportStatic {\n connect(config?: TransportConfig): Promise<TransportHID>;\n connectDevice?(\n device: KeystoneUsbDeviceDescriptor,\n config?: TransportConfig\n ): Promise<TransportHID>;\n getKeystoneDevices(): Promise<ReadonlyArray<KeystoneUsbDeviceDescriptor>>;\n requestPermission?(): Promise<void>;\n isSupported(): Promise<boolean>;\n /** Release transport-lifetime resources owned by the platform wrapper. */\n disposeTransport?(transport: TransportHID): void;\n}\n\ninterface KeystoneUr {\n urType: string;\n urData: string;\n}\n\ninterface UsbSession {\n transport: TransportHID;\n mfp?: string;\n dispose(): void;\n}\n\ninterface OpenedUsbTransport {\n transport: TransportHID;\n bindSessionId(sessionId: string): void;\n dispose(): void;\n}\n\ntype KeystoneUsbConnectInput =\n | { searchTargetId: string; expectedMasterFingerprint?: never }\n | { searchTargetId?: never; expectedMasterFingerprint: string }\n | { searchTargetId?: never; expectedMasterFingerprint?: never };\n\nconst DEFAULT_TIMEOUT_MS = 100_000; // SDK's own raw default is 15s (\"may need users' action on the device\") — too short for real confirmation.\n\nconst KEYSTONE_PUBLIC_DATA_UR_TYPE = 'qr-hardware-call';\n\nfunction toUrEncoded(ur: KeystoneUr): string {\n return new UREncoder(new UR(Buffer.from(ur.urData, 'hex'), ur.urType), Infinity)\n .nextPart()\n .toUpperCase();\n}\n\nfunction fromUrEncoded(encoded: unknown): KeystoneUr {\n // Accept either one complete UR or multiple whitespace-separated parts.\n // The transport owns EAPDU framing; this function only validates and joins\n // the BC-UR text present in that response.\n const text = typeof encoded === 'string' ? encoded.trim() : '';\n const parts = text.split(/\\s+/).filter(Boolean);\n const decoder = new URDecoder();\n for (const part of parts) {\n decoder.receivePart(part);\n if (decoder.isComplete()) break;\n }\n if (!parts.length || !decoder.isComplete() || !decoder.isSuccess()) {\n const shape =\n typeof encoded === 'string'\n ? `string len=${text.length} parts=${parts.length}`\n : typeof encoded;\n throw createHwkError({\n code: HardwareErrorCode.PayloadTooLarge,\n message: `Keystone USB returned an incomplete BC-UR response (${shape})`,\n origin: 'device',\n });\n }\n const ur = decoder.resultUR();\n return { urType: ur.type, urData: ur.cbor.toString('hex') };\n}\n\n/**\n * `IConnector` for Keystone over USB. Each transport call is its own\n * open/claim/transfer/close cycle, so `connect()` only caches the transport\n * object. `resolveUr` carries the same UR payloads as the QR channel.\n */\nexport class KeystoneUsbConnectorBase implements IConnector {\n readonly connectionType = 'usb' as const;\n\n private readonly transportClass: KeystoneUsbTransportStatic;\n\n private readonly timeoutMs: number;\n\n private readonly emitter = new TypedEventEmitter<ConnectorEventMap>();\n\n private readonly sessions = new Map<string, UsbSession>();\n\n private readonly discoveredDevices = new Map<string, KeystoneUsbDeviceDescriptor>();\n\n private readonly availabilityDevices = new Map<string, KeystoneUsbDeviceDescriptor>();\n\n private activeDiscoveryGeneration: symbol | undefined;\n\n private activeAvailabilityGeneration: symbol | undefined;\n\n // Keystone may re-enumerate on the USB bus between public-data exports,\n // which replaces the physical transport session without starting a new\n // user operation. Keep this UI-only state at connector lifetime scope so\n // an internal recovery does not look like another first connection.\n /** Wallets that already approved a public-data export, keyed by master fingerprint. */\n private readonly publicDataConfirmedWallets = new Set<string>();\n\n constructor(transportClass: KeystoneUsbTransportStatic, options?: { timeoutMs?: number }) {\n this.transportClass = transportClass;\n this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n private _invalidateDiscoverySnapshot(): void {\n this.activeDiscoveryGeneration = undefined;\n this.discoveredDevices.clear();\n }\n\n private _invalidateAvailabilitySnapshot(): void {\n this.activeAvailabilityGeneration = undefined;\n this.availabilityDevices.clear();\n }\n\n async searchDevices(options?: ConnectorSearchDevicesOptions): Promise<ConnectorDevice[]> {\n const isAvailabilitySearch = options?.purpose === 'availability';\n const discoveryGeneration = Symbol(\n isAvailabilitySearch ? 'keystone-usb-availability' : 'keystone-usb-discovery'\n );\n if (isAvailabilitySearch) {\n this._invalidateAvailabilitySnapshot();\n this.activeAvailabilityGeneration = discoveryGeneration;\n } else {\n this._invalidateDiscoverySnapshot();\n this.activeDiscoveryGeneration = discoveryGeneration;\n }\n const devices = await this.transportClass.getKeystoneDevices();\n // No mfp is available without opening+claiming the device. The target id\n // therefore identifies only this discovery snapshot; wallet identity is\n // learned and verified after connect. WebUSB can reopen the exact cached\n // descriptor. Platforms without that ability fail closed for multi-device\n // selection instead of silently opening a different unit.\n if (\n (isAvailabilitySearch\n ? this.activeAvailabilityGeneration\n : this.activeDiscoveryGeneration) !== discoveryGeneration\n ) {\n return [];\n }\n return devices.map(device => {\n const connectId = createHardwareSearchTargetId('keystone');\n (isAvailabilitySearch ? this.availabilityDevices : this.discoveredDevices).set(\n connectId,\n device\n );\n return {\n connectId,\n deviceId: '',\n name: usbDisplayName(device),\n connectionType: 'usb',\n serialNumber: device.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n // Everything enumeration can see, verbatim. Nothing here is normalized\n // or defaulted: a missing key means the device published no such string\n // descriptor. Promote a field onto the typed surface above once\n // something actually consumes it.\n raw: {\n serialNumber: device.serialNumber,\n productName: device.productName,\n manufacturerName: device.manufacturerName,\n vendorId: device.vendorId,\n productId: device.productId,\n deviceVersionMajor: device.deviceVersionMajor,\n deviceVersionMinor: device.deviceVersionMinor,\n deviceVersionSubminor: device.deviceVersionSubminor,\n usbVersionMajor: device.usbVersionMajor,\n usbVersionMinor: device.usbVersionMinor,\n usbVersionSubminor: device.usbVersionSubminor,\n deviceClass: device.deviceClass,\n deviceSubclass: device.deviceSubclass,\n deviceProtocol: device.deviceProtocol,\n },\n };\n });\n }\n\n async connect(searchTargetIdOrExpectedMasterFingerprint?: string): Promise<ConnectorSession> {\n return this._connectResolved(\n this._resolveConnectInput(searchTargetIdOrExpectedMasterFingerprint)\n );\n }\n\n async connectTarget(target: ConnectorConnectTarget): Promise<ConnectorSession> {\n switch (target.type) {\n case 'default':\n return this._connectResolved({});\n case 'search-target':\n return this._connectResolved({ searchTargetId: target.searchTargetId });\n case 'expected-device-identity': {\n const expectedMasterFingerprint = parseBip32MasterFingerprint(target.deviceIdentity);\n if (!expectedMasterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector requires a valid master fingerprint identity',\n });\n }\n return this._connectResolved({ expectedMasterFingerprint });\n }\n default:\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Unsupported Keystone USB connection target',\n });\n }\n }\n\n private async _connectResolved({\n searchTargetId,\n expectedMasterFingerprint,\n }: KeystoneUsbConnectInput): Promise<ConnectorSession> {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.Searching,\n payload: { sessionId: '' },\n });\n let selectedDevice = searchTargetId\n ? this.discoveredDevices.get(searchTargetId) ?? this.availabilityDevices.get(searchTargetId)\n : undefined;\n if (searchTargetId && !selectedDevice) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: `Keystone USB search target is not in the current scan: ${searchTargetId}`,\n recovery: { scope: 'search-target' },\n });\n }\n let openedTransport: OpenedUsbTransport | undefined;\n try {\n let config: Awaited<ReturnType<KeystoneUsbConnectorBase['_readAppConfig']>>;\n\n if (expectedMasterFingerprint && !searchTargetId) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (!this.transportClass.connectDevice && availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'This platform cannot identify one of multiple connected Keystone devices',\n });\n }\n\n if (this.transportClass.connectDevice) {\n let readableDeviceFound = false;\n let lastError: unknown;\n let matched:\n | {\n device: KeystoneUsbDeviceDescriptor;\n opened: OpenedUsbTransport;\n config: Awaited<ReturnType<KeystoneUsbConnectorBase['_readAppConfig']>>;\n }\n | undefined;\n for (const device of availableDevices) {\n let candidate: OpenedUsbTransport | undefined;\n try {\n // eslint-disable-next-line no-await-in-loop\n candidate = await this._openTransport(device);\n // eslint-disable-next-line no-await-in-loop\n const candidateConfig = await this._readAppConfig(candidate.transport);\n readableDeviceFound = true;\n if (candidateConfig.mfp?.toLowerCase() === expectedMasterFingerprint.toLowerCase()) {\n matched = { device, opened: candidate, config: candidateConfig };\n break;\n }\n candidate.dispose();\n } catch (error) {\n candidate?.dispose();\n lastError = error;\n }\n }\n if (!matched) {\n if (!readableDeviceFound && lastError) throw mapKeystoneUsbError(lastError);\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message: `No connected Keystone wallet matches fingerprint ${expectedMasterFingerprint}`,\n });\n }\n selectedDevice = matched.device;\n openedTransport = matched.opened;\n config = matched.config;\n } else {\n openedTransport = await this._openTransport();\n config = await this._readAppConfig(openedTransport.transport);\n }\n } else {\n if (!selectedDevice && this.transportClass.connectDevice) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (availableDevices.length === 0) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: 'No Keystone USB device is available',\n });\n }\n if (availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'Select a Keystone USB device before connecting',\n });\n }\n [selectedDevice] = availableDevices;\n }\n if (selectedDevice && !this.transportClass.connectDevice) {\n const availableDevices = await this.transportClass.getKeystoneDevices();\n if (availableDevices.length > 1) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceOneDeviceOnly,\n message: 'This platform cannot target one of multiple connected Keystone devices',\n });\n }\n }\n openedTransport = await this._openTransport(selectedDevice);\n config = await this._readAppConfig(openedTransport.transport);\n }\n\n if (!openedTransport) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: 'Keystone USB transport was not opened',\n });\n }\n const activeOpenedTransport = openedTransport;\n const { transport } = activeOpenedTransport;\n if (\n expectedMasterFingerprint &&\n config.mfp &&\n config.mfp.toLowerCase() !== expectedMasterFingerprint.toLowerCase()\n ) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message: `Connected Keystone wallet (mfp ${config.mfp}) does not match the requested wallet fingerprint (${expectedMasterFingerprint})`,\n });\n }\n const sessionId = createHardwareLinkId('keystone');\n activeOpenedTransport.bindSessionId(sessionId);\n this.sessions.set(sessionId, {\n transport,\n mfp: config.mfp,\n dispose: () => activeOpenedTransport.dispose(),\n });\n openedTransport = undefined;\n const device: ConnectorDevice = {\n connectId: sessionId,\n deviceId: '',\n name: selectedDevice ? usbDisplayName(selectedDevice) : 'Keystone',\n model: selectedDevice?.productName,\n connectionType: 'usb',\n serialNumber: selectedDevice?.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n raw: { masterFingerprint: config.mfp },\n };\n this.emitter.emit('device-connect', { device });\n return {\n sessionId,\n deviceInfo: this._toDeviceInfo(device, config.version, config.mfp),\n };\n } catch (err) {\n openedTransport?.dispose();\n throw mapKeystoneUsbError(err);\n }\n }\n\n private async _openTransport(device?: KeystoneUsbDeviceDescriptor): Promise<OpenedUsbTransport> {\n // A rejected candidate can keep its USB disconnect listener. Give every\n // descriptor a separate holder so it cannot retire a later matched session.\n let connectedSessionId: string | undefined;\n const transportConfig: TransportConfig = {\n timeout: this.timeoutMs,\n disconnectListener: () =>\n connectedSessionId ? this.disconnect(connectedSessionId) : undefined,\n };\n const transport = await (device && this.transportClass.connectDevice\n ? this.transportClass.connectDevice(device, transportConfig)\n : this.transportClass.connect(transportConfig));\n let disposed = false;\n return {\n transport,\n bindSessionId(sessionId: string) {\n connectedSessionId = sessionId;\n },\n dispose: () => {\n if (disposed) return;\n disposed = true;\n this.transportClass.disposeTransport?.(transport);\n },\n };\n }\n\n private _resolveConnectInput(value?: string): KeystoneUsbConnectInput {\n if (!value) return {};\n\n const runtimeId = parseHardwareRuntimeId(value);\n if (runtimeId) {\n // Whether this particular target is ours is settled by the snapshot\n // lookup in `_connectResolved`, not by anything encoded in the id.\n if (runtimeId.kind === 'search-target' && runtimeId.vendor === 'keystone') {\n return { searchTargetId: value };\n }\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector received an incompatible hardware runtime id',\n params: { kind: runtimeId.kind, vendor: runtimeId.vendor },\n });\n }\n\n if (hasHardwareRuntimeIdPrefix(value)) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector received a malformed hardware runtime id',\n });\n }\n\n const expectedMasterFingerprint = parseBip32MasterFingerprint(value);\n if (!expectedMasterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.InvalidParams,\n message: 'Keystone USB connector requires a search target or master fingerprint',\n });\n }\n return { expectedMasterFingerprint };\n }\n\n disconnect(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n this.sessions.delete(sessionId);\n if (session) {\n session.dispose();\n // No persistent claim to release (see class doc) — nothing to await\n // here beyond letting the underlying transport's own per-call close\n // run its course, which already happened on the last send().\n this.emitter.emit('device-disconnect', { connectId: sessionId });\n }\n return Promise.resolve();\n }\n\n /** Keep the vendor transport as the EAPDU framing authority. */\n private async _sendResolveUr(transport: TransportHID, encodedUr: string): Promise<KeystoneUr> {\n const response = await transport.send<{ payload: string }>(Actions.CMD_RESOLVE_UR, encodedUr);\n return fromUrEncoded(response.payload);\n }\n\n async call(sessionId: string, method: string, params: unknown): Promise<ConnectorCallResult> {\n const session = this.sessions.get(sessionId);\n if (!session) {\n return {\n success: false,\n error: serializeConnectorError(\n createHwkError({\n code: HardwareErrorCode.DeviceNotFound,\n message: `Unknown Keystone USB session: ${sessionId}`,\n })\n ),\n };\n }\n\n try {\n switch (method) {\n case 'resolveUr': {\n const { urType, urData } = params as KeystoneUr;\n const encoded = toUrEncoded({ urType, urData });\n const isPublicDataRequest = urType === KEYSTONE_PUBLIC_DATA_UR_TYPE;\n // Public-data export asks for approval once per wallet: internal USB\n // re-enumeration must not reopen the toast, but a different device\n // has not approved anything yet. Signing always confirms.\n const publicDataKey = session.mfp?.toLowerCase() ?? sessionId;\n const shouldShowConfirmation =\n !isPublicDataRequest || !this.publicDataConfirmedWallets.has(publicDataKey);\n if (shouldShowConfirmation) {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.ConfirmOnDevice,\n payload: { sessionId },\n });\n }\n try {\n const response = await this._sendResolveUr(session.transport, encoded);\n if (isPublicDataRequest) {\n this.publicDataConfirmedWallets.add(publicDataKey);\n }\n return success(response);\n } finally {\n if (shouldShowConfirmation) {\n this.emitter.emit('ui-event', {\n type: EConnectorInteraction.InteractionComplete,\n payload: { sessionId },\n });\n }\n }\n }\n case 'checkLockStatus': {\n const response = await session.transport.send<{ payload: boolean }>(\n Actions.CMD_CHECK_LOCK_STATUS,\n ''\n );\n // Naming-only assumption (no positive-case fixture to confirm\n // against) — `checkDeviceLockStatus`'s own name is the only\n // evidence `true` means locked; verify against real hardware.\n return success({ locked: response.payload });\n }\n case 'getAppConfig': {\n const config = await this._readAppConfig(session.transport);\n return success(config);\n }\n default:\n return {\n success: false,\n error: serializeConnectorError(\n createHwkError({\n code: HardwareErrorCode.MethodNotSupported,\n message: `Unknown Keystone USB connector method: ${method}`,\n })\n ),\n };\n }\n } catch (err) {\n return { success: false, error: serializeConnectorError(mapKeystoneUsbError(err)) };\n }\n }\n\n // No protocol-level cancel exists over USB (verified: nothing in the SDK's\n // public surface can interrupt an in-flight transferIn/transferOut) — the\n // best this connector can do is stop waiting on its side; the underlying\n // `send()` promise still settles (or times out) on its own.\n cancel(_sessionId: string): Promise<void> {\n return Promise.resolve();\n }\n\n // Keystone USB never relays PIN/passphrase through the host — entry always\n // happens on the device's own touchscreen — so there is nothing to answer.\n uiResponse(_response: UiResponseEvent): void {}\n\n on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void {\n this.emitter.on(event, handler);\n }\n\n off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void {\n this.emitter.off(event, handler);\n }\n\n reset(): void {\n for (const session of this.sessions.values()) {\n session.dispose();\n }\n this.sessions.clear();\n this._invalidateDiscoverySnapshot();\n this._invalidateAvailabilitySnapshot();\n this.publicDataConfirmedWallets.clear();\n }\n\n private async _readAppConfig(\n transport: TransportHID\n ): Promise<{ version?: string; mfp?: string }> {\n const response = await transport.send<Record<string, unknown>>(\n Actions.CMD_GET_DEVICE_VERSION,\n ''\n );\n const rawMasterFingerprint = response.walletMFP;\n const masterFingerprint = parseBip32MasterFingerprint(rawMasterFingerprint);\n if (rawMasterFingerprint !== undefined && !masterFingerprint) {\n throw createHwkError({\n code: HardwareErrorCode.DeviceMismatch,\n message:\n 'Keystone returned an invalid master fingerprint; expected exactly 4 bytes (8 hex characters)',\n });\n }\n return {\n version: typeof response.firmwareVersion === 'string' ? response.firmwareVersion : undefined,\n mfp: masterFingerprint,\n };\n }\n\n private _toDeviceInfo(\n device: ConnectorDevice,\n firmwareVersion?: string,\n masterFingerprint?: string\n ) {\n return {\n vendor: 'keystone' as const,\n // The device publishes its model as a USB string descriptor\n // (`productName`, e.g. \"Keystone 3 Pro\"); `device.model` carries it here.\n // Empty rather than a literal 'unknown' when it says nothing, so the\n // host's own default-name fallback can win instead of a fake model\n // reaching the UI and the persisted device settings.\n model: device.model ?? '',\n modelName: device.modelName,\n firmwareVersion: firmwareVersion ?? '0.0.0',\n deviceId: device.deviceId,\n connectId: device.connectId,\n connectionType: 'usb' as const,\n serialNumber: device.serialNumber,\n capabilities: { persistentDeviceIdentity: false },\n raw: { masterFingerprint },\n };\n }\n}\n"]}