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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,554 @@
1
+ // src/errors.ts
2
+ import { HardwareErrorCode, createHwkError } from "@onekeyfe/hwk-adapter-core";
3
+ import { Status } from "@keystonehq/hw-transport-error";
4
+ var DEVICE_STATUS_MAP = {
5
+ [Status.PRS_PARSING_REJECTED]: HardwareErrorCode.UserRejected,
6
+ [Status.PRS_PARSING_DISALLOWED]: HardwareErrorCode.DeviceLocked,
7
+ [Status.PRS_PARSING_MISMATCHED_WALLET]: HardwareErrorCode.DeviceMismatch,
8
+ [Status.PRS_EXPORT_ADDRESS_REJECTED]: HardwareErrorCode.UserRejected,
9
+ [Status.PRS_EXPORT_ADDRESS_DISALLOWED]: HardwareErrorCode.DeviceLocked
10
+ };
11
+ var CLIENT_STATUS_MAP = {
12
+ [Status.ERR_DEVICE_NOT_OPENED]: HardwareErrorCode.DeviceNotFound,
13
+ [Status.ERR_DEVICE_NOT_FOUND]: HardwareErrorCode.DeviceNotFound,
14
+ [Status.ERR_TIMEOUT]: HardwareErrorCode.OperationTimeout,
15
+ [Status.ERR_DATA_TOO_LARGE]: HardwareErrorCode.PayloadTooLarge,
16
+ [Status.ERR_NOT_SUPPORTED]: HardwareErrorCode.TransportNotAvailable
17
+ };
18
+ function mapKeystoneUsbError(err) {
19
+ const domName = err && typeof err === "object" && typeof err.name === "string" ? err.name : void 0;
20
+ if (err && typeof err === "object" && typeof err.code === "number" && err.code >= 1e4 && err.code <= 99999) {
21
+ return err;
22
+ }
23
+ const statusCode = err && typeof err === "object" && typeof err.transportErrorCode === "number" ? err.transportErrorCode : void 0;
24
+ const message = err instanceof Error ? err.message : String(err);
25
+ if (statusCode !== void 0) {
26
+ const deviceCode = DEVICE_STATUS_MAP[statusCode];
27
+ const code = deviceCode ?? CLIENT_STATUS_MAP[statusCode] ?? HardwareErrorCode.TransportError;
28
+ let origin;
29
+ if (deviceCode !== void 0) {
30
+ origin = "device";
31
+ } else if (code !== HardwareErrorCode.OperationTimeout) {
32
+ origin = "transport";
33
+ }
34
+ return createHwkError({
35
+ 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
39
+ // status codes came from the framing/pipe layer — EXCEPT the timeout,
40
+ // which is genuinely two-faced (the device may be sitting on a confirm
41
+ // screen waiting for a human, or the pipe may be dead) and stays
42
+ // unlabeled rather than mislabeled.
43
+ origin,
44
+ params: { statusCode, details: err.details }
45
+ });
46
+ }
47
+ if (domName === "NotFoundError" || domName === "NetworkError") {
48
+ return createHwkError({
49
+ code: HardwareErrorCode.DeviceNotFound,
50
+ message,
51
+ origin: "transport",
52
+ params: { domExceptionName: domName }
53
+ });
54
+ }
55
+ if (domName === "SecurityError" || domName === "NotAllowedError") {
56
+ return createHwkError({
57
+ code: HardwareErrorCode.DevicePermissionDenied,
58
+ message,
59
+ origin: "host",
60
+ params: { domExceptionName: domName }
61
+ });
62
+ }
63
+ if (domName === "InvalidStateError" || domName === "AbortError") {
64
+ return createHwkError({
65
+ code: HardwareErrorCode.DeviceBusy,
66
+ message,
67
+ origin: "transport",
68
+ params: { domExceptionName: domName }
69
+ });
70
+ }
71
+ return createHwkError({ code: HardwareErrorCode.TransportError, message, origin: "transport" });
72
+ }
73
+
74
+ // src/KeystoneUsbConnectorBase.ts
75
+ import { UR, URDecoder, UREncoder } from "@ngraveio/bc-ur";
76
+ import { Actions } from "@keystonehq/hw-transport-usb";
77
+ import {
78
+ EConnectorInteraction,
79
+ HardwareErrorCode as HardwareErrorCode2,
80
+ TypedEventEmitter,
81
+ createHardwareLinkId,
82
+ createHardwareSearchTargetId,
83
+ createHwkError as createHwkError2,
84
+ hasHardwareRuntimeIdPrefix,
85
+ parseBip32MasterFingerprint,
86
+ parseHardwareRuntimeId,
87
+ serializeConnectorError,
88
+ success
89
+ } from "@onekeyfe/hwk-adapter-core";
90
+ function usbDisplayName(device) {
91
+ const base = device.productName?.trim() || "Keystone";
92
+ const suffix = device.serialNumber?.split("-").pop()?.trim();
93
+ return suffix ? `${base} ${suffix}` : base;
94
+ }
95
+ var DEFAULT_TIMEOUT_MS = 1e5;
96
+ var KEYSTONE_PUBLIC_DATA_UR_TYPE = "qr-hardware-call";
97
+ function toUrEncoded(ur) {
98
+ return new UREncoder(new UR(Buffer.from(ur.urData, "hex"), ur.urType), Infinity).nextPart().toUpperCase();
99
+ }
100
+ function fromUrEncoded(encoded) {
101
+ const text = typeof encoded === "string" ? encoded.trim() : "";
102
+ const parts = text.split(/\s+/).filter(Boolean);
103
+ const decoder = new URDecoder();
104
+ for (const part of parts) {
105
+ decoder.receivePart(part);
106
+ if (decoder.isComplete()) break;
107
+ }
108
+ if (!parts.length || !decoder.isComplete() || !decoder.isSuccess()) {
109
+ const shape = typeof encoded === "string" ? `string len=${text.length} parts=${parts.length}` : typeof encoded;
110
+ throw createHwkError2({
111
+ code: HardwareErrorCode2.PayloadTooLarge,
112
+ message: `Keystone USB returned an incomplete BC-UR response (${shape})`,
113
+ origin: "device"
114
+ });
115
+ }
116
+ const ur = decoder.resultUR();
117
+ return { urType: ur.type, urData: ur.cbor.toString("hex") };
118
+ }
119
+ var KeystoneUsbConnectorBase = class {
120
+ constructor(transportClass, options) {
121
+ this.connectionType = "usb";
122
+ this.emitter = new TypedEventEmitter();
123
+ this.sessions = /* @__PURE__ */ new Map();
124
+ this.discoveredDevices = /* @__PURE__ */ new Map();
125
+ this.availabilityDevices = /* @__PURE__ */ new Map();
126
+ // Keystone may re-enumerate on the USB bus between public-data exports,
127
+ // which replaces the physical transport session without starting a new
128
+ // user operation. Keep this UI-only state at connector lifetime scope so
129
+ // an internal recovery does not look like another first connection.
130
+ /** Wallets that already approved a public-data export, keyed by master fingerprint. */
131
+ this.publicDataConfirmedWallets = /* @__PURE__ */ new Set();
132
+ this.transportClass = transportClass;
133
+ this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
134
+ }
135
+ _invalidateDiscoverySnapshot() {
136
+ this.activeDiscoveryGeneration = void 0;
137
+ this.discoveredDevices.clear();
138
+ }
139
+ _invalidateAvailabilitySnapshot() {
140
+ this.activeAvailabilityGeneration = void 0;
141
+ this.availabilityDevices.clear();
142
+ }
143
+ async searchDevices(options) {
144
+ const isAvailabilitySearch = options?.purpose === "availability";
145
+ const discoveryGeneration = /* @__PURE__ */ Symbol(
146
+ isAvailabilitySearch ? "keystone-usb-availability" : "keystone-usb-discovery"
147
+ );
148
+ if (isAvailabilitySearch) {
149
+ this._invalidateAvailabilitySnapshot();
150
+ this.activeAvailabilityGeneration = discoveryGeneration;
151
+ } else {
152
+ this._invalidateDiscoverySnapshot();
153
+ this.activeDiscoveryGeneration = discoveryGeneration;
154
+ }
155
+ const devices = await this.transportClass.getKeystoneDevices();
156
+ if ((isAvailabilitySearch ? this.activeAvailabilityGeneration : this.activeDiscoveryGeneration) !== discoveryGeneration) {
157
+ return [];
158
+ }
159
+ return devices.map((device) => {
160
+ const connectId = createHardwareSearchTargetId("keystone");
161
+ (isAvailabilitySearch ? this.availabilityDevices : this.discoveredDevices).set(
162
+ connectId,
163
+ device
164
+ );
165
+ return {
166
+ connectId,
167
+ deviceId: "",
168
+ name: usbDisplayName(device),
169
+ connectionType: "usb",
170
+ serialNumber: device.serialNumber,
171
+ capabilities: { persistentDeviceIdentity: false },
172
+ // Everything enumeration can see, verbatim. Nothing here is normalized
173
+ // or defaulted: a missing key means the device published no such string
174
+ // descriptor. Promote a field onto the typed surface above once
175
+ // something actually consumes it.
176
+ raw: {
177
+ serialNumber: device.serialNumber,
178
+ productName: device.productName,
179
+ manufacturerName: device.manufacturerName,
180
+ vendorId: device.vendorId,
181
+ productId: device.productId,
182
+ deviceVersionMajor: device.deviceVersionMajor,
183
+ deviceVersionMinor: device.deviceVersionMinor,
184
+ deviceVersionSubminor: device.deviceVersionSubminor,
185
+ usbVersionMajor: device.usbVersionMajor,
186
+ usbVersionMinor: device.usbVersionMinor,
187
+ usbVersionSubminor: device.usbVersionSubminor,
188
+ deviceClass: device.deviceClass,
189
+ deviceSubclass: device.deviceSubclass,
190
+ deviceProtocol: device.deviceProtocol
191
+ }
192
+ };
193
+ });
194
+ }
195
+ async connect(searchTargetIdOrExpectedMasterFingerprint) {
196
+ return this._connectResolved(
197
+ this._resolveConnectInput(searchTargetIdOrExpectedMasterFingerprint)
198
+ );
199
+ }
200
+ async connectTarget(target) {
201
+ switch (target.type) {
202
+ case "default":
203
+ return this._connectResolved({});
204
+ case "search-target":
205
+ return this._connectResolved({ searchTargetId: target.searchTargetId });
206
+ case "expected-device-identity": {
207
+ const expectedMasterFingerprint = parseBip32MasterFingerprint(target.deviceIdentity);
208
+ if (!expectedMasterFingerprint) {
209
+ throw createHwkError2({
210
+ code: HardwareErrorCode2.InvalidParams,
211
+ message: "Keystone USB connector requires a valid master fingerprint identity"
212
+ });
213
+ }
214
+ return this._connectResolved({ expectedMasterFingerprint });
215
+ }
216
+ default:
217
+ throw createHwkError2({
218
+ code: HardwareErrorCode2.InvalidParams,
219
+ message: "Unsupported Keystone USB connection target"
220
+ });
221
+ }
222
+ }
223
+ async _connectResolved({
224
+ searchTargetId,
225
+ expectedMasterFingerprint
226
+ }) {
227
+ this.emitter.emit("ui-event", {
228
+ type: EConnectorInteraction.Searching,
229
+ payload: { sessionId: "" }
230
+ });
231
+ let selectedDevice = searchTargetId ? this.discoveredDevices.get(searchTargetId) ?? this.availabilityDevices.get(searchTargetId) : void 0;
232
+ if (searchTargetId && !selectedDevice) {
233
+ throw createHwkError2({
234
+ code: HardwareErrorCode2.DeviceNotFound,
235
+ message: `Keystone USB search target is not in the current scan: ${searchTargetId}`,
236
+ recovery: { scope: "search-target" }
237
+ });
238
+ }
239
+ let openedTransport;
240
+ try {
241
+ let config;
242
+ if (expectedMasterFingerprint && !searchTargetId) {
243
+ const availableDevices = await this.transportClass.getKeystoneDevices();
244
+ if (!this.transportClass.connectDevice && availableDevices.length > 1) {
245
+ throw createHwkError2({
246
+ code: HardwareErrorCode2.DeviceOneDeviceOnly,
247
+ message: "This platform cannot identify one of multiple connected Keystone devices"
248
+ });
249
+ }
250
+ if (this.transportClass.connectDevice) {
251
+ let readableDeviceFound = false;
252
+ let lastError;
253
+ let matched;
254
+ for (const device2 of availableDevices) {
255
+ let candidate;
256
+ try {
257
+ candidate = await this._openTransport(device2);
258
+ const candidateConfig = await this._readAppConfig(candidate.transport);
259
+ readableDeviceFound = true;
260
+ if (candidateConfig.mfp?.toLowerCase() === expectedMasterFingerprint.toLowerCase()) {
261
+ matched = { device: device2, opened: candidate, config: candidateConfig };
262
+ break;
263
+ }
264
+ candidate.dispose();
265
+ } catch (error) {
266
+ candidate?.dispose();
267
+ lastError = error;
268
+ }
269
+ }
270
+ if (!matched) {
271
+ if (!readableDeviceFound && lastError) throw mapKeystoneUsbError(lastError);
272
+ throw createHwkError2({
273
+ code: HardwareErrorCode2.DeviceMismatch,
274
+ message: `No connected Keystone wallet matches fingerprint ${expectedMasterFingerprint}`
275
+ });
276
+ }
277
+ selectedDevice = matched.device;
278
+ openedTransport = matched.opened;
279
+ config = matched.config;
280
+ } else {
281
+ openedTransport = await this._openTransport();
282
+ config = await this._readAppConfig(openedTransport.transport);
283
+ }
284
+ } else {
285
+ if (!selectedDevice && this.transportClass.connectDevice) {
286
+ const availableDevices = await this.transportClass.getKeystoneDevices();
287
+ if (availableDevices.length === 0) {
288
+ throw createHwkError2({
289
+ code: HardwareErrorCode2.DeviceNotFound,
290
+ message: "No Keystone USB device is available"
291
+ });
292
+ }
293
+ if (availableDevices.length > 1) {
294
+ throw createHwkError2({
295
+ code: HardwareErrorCode2.DeviceOneDeviceOnly,
296
+ message: "Select a Keystone USB device before connecting"
297
+ });
298
+ }
299
+ [selectedDevice] = availableDevices;
300
+ }
301
+ if (selectedDevice && !this.transportClass.connectDevice) {
302
+ const availableDevices = await this.transportClass.getKeystoneDevices();
303
+ if (availableDevices.length > 1) {
304
+ throw createHwkError2({
305
+ code: HardwareErrorCode2.DeviceOneDeviceOnly,
306
+ message: "This platform cannot target one of multiple connected Keystone devices"
307
+ });
308
+ }
309
+ }
310
+ openedTransport = await this._openTransport(selectedDevice);
311
+ config = await this._readAppConfig(openedTransport.transport);
312
+ }
313
+ if (!openedTransport) {
314
+ throw createHwkError2({
315
+ code: HardwareErrorCode2.DeviceNotFound,
316
+ message: "Keystone USB transport was not opened"
317
+ });
318
+ }
319
+ const activeOpenedTransport = openedTransport;
320
+ const { transport } = activeOpenedTransport;
321
+ if (expectedMasterFingerprint && config.mfp && config.mfp.toLowerCase() !== expectedMasterFingerprint.toLowerCase()) {
322
+ throw createHwkError2({
323
+ code: HardwareErrorCode2.DeviceMismatch,
324
+ message: `Connected Keystone wallet (mfp ${config.mfp}) does not match the requested wallet fingerprint (${expectedMasterFingerprint})`
325
+ });
326
+ }
327
+ const sessionId = createHardwareLinkId("keystone");
328
+ activeOpenedTransport.bindSessionId(sessionId);
329
+ this.sessions.set(sessionId, {
330
+ transport,
331
+ mfp: config.mfp,
332
+ dispose: () => activeOpenedTransport.dispose()
333
+ });
334
+ openedTransport = void 0;
335
+ const device = {
336
+ connectId: sessionId,
337
+ deviceId: "",
338
+ name: selectedDevice ? usbDisplayName(selectedDevice) : "Keystone",
339
+ model: selectedDevice?.productName,
340
+ connectionType: "usb",
341
+ serialNumber: selectedDevice?.serialNumber,
342
+ capabilities: { persistentDeviceIdentity: false },
343
+ raw: { masterFingerprint: config.mfp }
344
+ };
345
+ this.emitter.emit("device-connect", { device });
346
+ return {
347
+ sessionId,
348
+ deviceInfo: this._toDeviceInfo(device, config.version, config.mfp)
349
+ };
350
+ } catch (err) {
351
+ openedTransport?.dispose();
352
+ throw mapKeystoneUsbError(err);
353
+ }
354
+ }
355
+ async _openTransport(device) {
356
+ let connectedSessionId;
357
+ const transportConfig = {
358
+ timeout: this.timeoutMs,
359
+ disconnectListener: () => connectedSessionId ? this.disconnect(connectedSessionId) : void 0
360
+ };
361
+ const transport = await (device && this.transportClass.connectDevice ? this.transportClass.connectDevice(device, transportConfig) : this.transportClass.connect(transportConfig));
362
+ let disposed = false;
363
+ return {
364
+ transport,
365
+ bindSessionId(sessionId) {
366
+ connectedSessionId = sessionId;
367
+ },
368
+ dispose: () => {
369
+ if (disposed) return;
370
+ disposed = true;
371
+ this.transportClass.disposeTransport?.(transport);
372
+ }
373
+ };
374
+ }
375
+ _resolveConnectInput(value) {
376
+ if (!value) return {};
377
+ const runtimeId = parseHardwareRuntimeId(value);
378
+ if (runtimeId) {
379
+ if (runtimeId.kind === "search-target" && runtimeId.vendor === "keystone") {
380
+ return { searchTargetId: value };
381
+ }
382
+ throw createHwkError2({
383
+ code: HardwareErrorCode2.InvalidParams,
384
+ message: "Keystone USB connector received an incompatible hardware runtime id",
385
+ params: { kind: runtimeId.kind, vendor: runtimeId.vendor }
386
+ });
387
+ }
388
+ if (hasHardwareRuntimeIdPrefix(value)) {
389
+ throw createHwkError2({
390
+ code: HardwareErrorCode2.InvalidParams,
391
+ message: "Keystone USB connector received a malformed hardware runtime id"
392
+ });
393
+ }
394
+ const expectedMasterFingerprint = parseBip32MasterFingerprint(value);
395
+ if (!expectedMasterFingerprint) {
396
+ throw createHwkError2({
397
+ code: HardwareErrorCode2.InvalidParams,
398
+ message: "Keystone USB connector requires a search target or master fingerprint"
399
+ });
400
+ }
401
+ return { expectedMasterFingerprint };
402
+ }
403
+ disconnect(sessionId) {
404
+ const session = this.sessions.get(sessionId);
405
+ this.sessions.delete(sessionId);
406
+ if (session) {
407
+ session.dispose();
408
+ this.emitter.emit("device-disconnect", { connectId: sessionId });
409
+ }
410
+ return Promise.resolve();
411
+ }
412
+ /** Keep the vendor transport as the EAPDU framing authority. */
413
+ async _sendResolveUr(transport, encodedUr) {
414
+ const response = await transport.send(Actions.CMD_RESOLVE_UR, encodedUr);
415
+ return fromUrEncoded(response.payload);
416
+ }
417
+ async call(sessionId, method, params) {
418
+ const session = this.sessions.get(sessionId);
419
+ if (!session) {
420
+ return {
421
+ success: false,
422
+ error: serializeConnectorError(
423
+ createHwkError2({
424
+ code: HardwareErrorCode2.DeviceNotFound,
425
+ message: `Unknown Keystone USB session: ${sessionId}`
426
+ })
427
+ )
428
+ };
429
+ }
430
+ try {
431
+ switch (method) {
432
+ case "resolveUr": {
433
+ const { urType, urData } = params;
434
+ const encoded = toUrEncoded({ urType, urData });
435
+ const isPublicDataRequest = urType === KEYSTONE_PUBLIC_DATA_UR_TYPE;
436
+ const publicDataKey = session.mfp?.toLowerCase() ?? sessionId;
437
+ const shouldShowConfirmation = !isPublicDataRequest || !this.publicDataConfirmedWallets.has(publicDataKey);
438
+ if (shouldShowConfirmation) {
439
+ this.emitter.emit("ui-event", {
440
+ type: EConnectorInteraction.ConfirmOnDevice,
441
+ payload: { sessionId }
442
+ });
443
+ }
444
+ try {
445
+ const response = await this._sendResolveUr(session.transport, encoded);
446
+ if (isPublicDataRequest) {
447
+ this.publicDataConfirmedWallets.add(publicDataKey);
448
+ }
449
+ return success(response);
450
+ } finally {
451
+ if (shouldShowConfirmation) {
452
+ this.emitter.emit("ui-event", {
453
+ type: EConnectorInteraction.InteractionComplete,
454
+ payload: { sessionId }
455
+ });
456
+ }
457
+ }
458
+ }
459
+ case "checkLockStatus": {
460
+ const response = await session.transport.send(
461
+ Actions.CMD_CHECK_LOCK_STATUS,
462
+ ""
463
+ );
464
+ return success({ locked: response.payload });
465
+ }
466
+ case "getAppConfig": {
467
+ const config = await this._readAppConfig(session.transport);
468
+ return success(config);
469
+ }
470
+ default:
471
+ return {
472
+ success: false,
473
+ error: serializeConnectorError(
474
+ createHwkError2({
475
+ code: HardwareErrorCode2.MethodNotSupported,
476
+ message: `Unknown Keystone USB connector method: ${method}`
477
+ })
478
+ )
479
+ };
480
+ }
481
+ } catch (err) {
482
+ return { success: false, error: serializeConnectorError(mapKeystoneUsbError(err)) };
483
+ }
484
+ }
485
+ // No protocol-level cancel exists over USB (verified: nothing in the SDK's
486
+ // public surface can interrupt an in-flight transferIn/transferOut) — the
487
+ // best this connector can do is stop waiting on its side; the underlying
488
+ // `send()` promise still settles (or times out) on its own.
489
+ cancel(_sessionId) {
490
+ return Promise.resolve();
491
+ }
492
+ // Keystone USB never relays PIN/passphrase through the host — entry always
493
+ // happens on the device's own touchscreen — so there is nothing to answer.
494
+ uiResponse(_response) {
495
+ }
496
+ on(event, handler) {
497
+ this.emitter.on(event, handler);
498
+ }
499
+ off(event, handler) {
500
+ this.emitter.off(event, handler);
501
+ }
502
+ reset() {
503
+ for (const session of this.sessions.values()) {
504
+ session.dispose();
505
+ }
506
+ this.sessions.clear();
507
+ this._invalidateDiscoverySnapshot();
508
+ this._invalidateAvailabilitySnapshot();
509
+ this.publicDataConfirmedWallets.clear();
510
+ }
511
+ async _readAppConfig(transport) {
512
+ const response = await transport.send(
513
+ Actions.CMD_GET_DEVICE_VERSION,
514
+ ""
515
+ );
516
+ const rawMasterFingerprint = response.walletMFP;
517
+ const masterFingerprint = parseBip32MasterFingerprint(rawMasterFingerprint);
518
+ if (rawMasterFingerprint !== void 0 && !masterFingerprint) {
519
+ throw createHwkError2({
520
+ code: HardwareErrorCode2.DeviceMismatch,
521
+ message: "Keystone returned an invalid master fingerprint; expected exactly 4 bytes (8 hex characters)"
522
+ });
523
+ }
524
+ return {
525
+ version: typeof response.firmwareVersion === "string" ? response.firmwareVersion : void 0,
526
+ mfp: masterFingerprint
527
+ };
528
+ }
529
+ _toDeviceInfo(device, firmwareVersion, masterFingerprint) {
530
+ return {
531
+ vendor: "keystone",
532
+ // The device publishes its model as a USB string descriptor
533
+ // (`productName`, e.g. "Keystone 3 Pro"); `device.model` carries it here.
534
+ // Empty rather than a literal 'unknown' when it says nothing, so the
535
+ // host's own default-name fallback can win instead of a fake model
536
+ // reaching the UI and the persisted device settings.
537
+ model: device.model ?? "",
538
+ modelName: device.modelName,
539
+ firmwareVersion: firmwareVersion ?? "0.0.0",
540
+ deviceId: device.deviceId,
541
+ connectId: device.connectId,
542
+ connectionType: "usb",
543
+ serialNumber: device.serialNumber,
544
+ capabilities: { persistentDeviceIdentity: false },
545
+ raw: { masterFingerprint }
546
+ };
547
+ }
548
+ };
549
+
550
+ export {
551
+ mapKeystoneUsbError,
552
+ KeystoneUsbConnectorBase
553
+ };
554
+ //# sourceMappingURL=chunk-2B6OTW7M.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 * 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"]}