@onekeyfe/hd-transport-usb 1.1.27-alpha.41 → 1.1.27-alpha.6

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,5 @@
1
+ export declare const PACKET_SIZE = 64;
2
+ export declare const REPORT_ID = 63;
3
+ export declare const PAYLOAD_SIZE: number;
4
+ export declare const HEADER_LENGTH = 6;
5
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,WAAW,KAAK,CAAC;AAG9B,eAAO,MAAM,SAAS,KAAO,CAAC;AAG9B,eAAO,MAAM,YAAY,QAAkB,CAAC;AAG5C,eAAO,MAAM,aAAa,IAAI,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import * as transport from '@onekeyfe/hd-transport';
2
- import transport__default, { OneKeyDeviceInfo, AcquireInput, TransportCallOptions, ProtocolType } from '@onekeyfe/hd-transport';
2
+ import transport__default, { OneKeyDeviceInfo, AcquireInput } from '@onekeyfe/hd-transport';
3
3
  import EventEmitter from 'events';
4
4
 
5
+ declare const PACKET_SIZE = 64;
6
+
5
7
  declare class NodeUsbTransport {
6
8
  messages: ReturnType<typeof transport__default.parseConfigure> | undefined;
7
- messagesV2: ReturnType<typeof transport__default.parseConfigure> | undefined;
8
9
  name: string;
9
10
  version: string;
10
11
  configured: boolean;
@@ -13,13 +14,10 @@ declare class NodeUsbTransport {
13
14
  emitter?: EventEmitter;
14
15
  private serialToBusId;
15
16
  private openDevices;
16
- private deviceProtocol;
17
- private protocolV2Assemblers;
18
17
  private reconnectLocks;
19
18
  private cancelled;
20
19
  init(logger: any, emitter?: EventEmitter): Promise<string>;
21
20
  configure(signedData: any): Promise<void>;
22
- configureProtocolV2(signedData: any): void;
23
21
  listen(): void;
24
22
  stop(): void;
25
23
  post(path: string, name: string, data: Record<string, unknown>): Promise<void>;
@@ -32,30 +30,16 @@ declare class NodeUsbTransport {
32
30
  enumerate(): Promise<OneKeyDeviceInfo[]>;
33
31
  acquire(input: AcquireInput): Promise<string>;
34
32
  release(path: string, _onclose?: boolean): Promise<void>;
35
- private closeOpenDevice;
36
- call(path: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<transport.MessageFromOneKey>;
37
- private callProtocolV1;
33
+ call(path: string, name: string, data: Record<string, unknown>): Promise<transport.MessageFromOneKey>;
38
34
  cancel(): void;
39
35
  private getOpenDevice;
40
36
  private getErrorMessage;
41
37
  private isRetryableError;
42
- private getDeviceInterface;
43
38
  private reconnectForRetry;
44
39
  private sendAllChunksWithRetry;
45
40
  private transferInWithRetry;
46
41
  private openDevice;
47
- private createProtocolMismatchError;
48
- private createProtocolDetectionError;
49
- private detectProtocol;
50
- private resetConnectionAfterProbe;
51
- private withProtocolReadTimeout;
52
- private probeProtocolV1;
53
- private probeProtocolV2;
54
- private writeProtocolV2Frame;
55
- private receiveProtocolV2Frame;
56
- private callProtocolV2;
57
42
  private receiveData;
58
- getProtocolType(path: string): ProtocolType | undefined;
59
43
  }
60
44
 
61
- export { NodeUsbTransport as default };
45
+ export { PACKET_SIZE, NodeUsbTransport as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAgKhC,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEpE,IAAI,SAAsB;IAE1B,OAAO,SAAM;IAEb,UAAU,UAAS;IAEnB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAGvB,OAAO,CAAC,aAAa,CAA6B;IAGlD,OAAO,CAAC,WAAW,CAAiC;IAGpD,OAAO,CAAC,cAAc,CAAwC;IAG9D,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,cAAc,CAA0C;IAGhE,OAAO,CAAC,SAAS,CAAS;IAM1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAMxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAOzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAKnC,MAAM;IAIN,IAAI;IAQE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9E,IAAI,CAAC,IAAI,EAAE,MAAM;;;;;;IAiBjB,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA8BxC,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAqB7C,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;YAMhD,eAAe;IA6BvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAwClB,cAAc;IA0B5B,MAAM;IAWN,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,iBAAiB;YAmDX,sBAAsB;YAyCtB,mBAAmB;IAsCjC,OAAO,CAAC,UAAU;IA8DlB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;YA4Cd,yBAAyB;YAazB,uBAAuB;YA0CvB,eAAe;YAcf,eAAe;YAcf,oBAAoB;YA+BpB,sBAAsB;YAuCtB,cAAc;YAuCd,WAAW;IA6CzB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,SAA8B,MAAM,wBAAwB,CAAC;AAKpE,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AA0J7E,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAElE,IAAI,SAAsB;IAE1B,OAAO,SAAM;IAEb,UAAU,UAAS;IAEnB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAGvB,OAAO,CAAC,aAAa,CAA6B;IAGlD,OAAO,CAAC,WAAW,CAAiC;IAGpD,OAAO,CAAC,cAAc,CAA0C;IAGhE,OAAO,CAAC,SAAS,CAAS;IAM1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAMxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAOzB,MAAM;IAIN,IAAI;IAQE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9E,IAAI,CAAC,IAAI,EAAE,MAAM;;;;;;IAiBjB,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA8B9C,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBvC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BxD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAkCpE,MAAM;IAWN,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,iBAAiB;YAmDX,sBAAsB;YAyCtB,mBAAmB;IAsCjC,OAAO,CAAC,UAAU;YAgEJ,WAAW;CAkC1B;AAED,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
3
5
  var ByteBuffer = require('bytebuffer');
4
6
  var usb = require('usb');
5
7
  var transport = require('@onekeyfe/hd-transport');
@@ -59,11 +61,12 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
59
61
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
60
62
  };
61
63
 
62
- const { parseConfigure, ProtocolV1, check } = transport__default["default"];
63
- const PACKET_SIZE = transport.PROTOCOL_V1_USB_PACKET_SIZE;
64
- const REPORT_ID = transport.PROTOCOL_V1_REPORT_ID;
65
- const PAYLOAD_SIZE = transport.PROTOCOL_V1_CHUNK_PAYLOAD_SIZE;
66
- const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
64
+ const PACKET_SIZE = 64;
65
+ const REPORT_ID = 0x3f;
66
+ const PAYLOAD_SIZE = PACKET_SIZE - 1;
67
+ const HEADER_LENGTH = 6;
68
+
69
+ const { parseConfigure, buildEncodeBuffers, decodeProtocol, receiveOne, check } = transport__default["default"];
67
70
  const INTERFACE_NUMBER = 0;
68
71
  const ENDPOINT_IN = 0x81;
69
72
  const ENDPOINT_OUT = 0x01;
@@ -71,7 +74,6 @@ const TRANSFER_TIMEOUT_MS = 30000;
71
74
  const SERIAL_READ_TIMEOUT_MS = 5000;
72
75
  const PACKET_IO_MAX_RETRIES = 3;
73
76
  const PACKET_IO_RETRY_DELAY = 300;
74
- const PROTOCOL_PROBE_TIMEOUT = 1000;
75
77
  function getBusId(dev) {
76
78
  return `usb:${dev.busNumber}:${dev.deviceAddress}`;
77
79
  }
@@ -169,8 +171,6 @@ class NodeUsbTransport {
169
171
  this.isOutdated = false;
170
172
  this.serialToBusId = new Map();
171
173
  this.openDevices = new Map();
172
- this.deviceProtocol = new Map();
173
- this.protocolV2Assemblers = new Map();
174
174
  this.reconnectLocks = new Map();
175
175
  this.cancelled = false;
176
176
  }
@@ -185,11 +185,6 @@ class NodeUsbTransport {
185
185
  this.messages = messages;
186
186
  return Promise.resolve();
187
187
  }
188
- configureProtocolV2(signedData) {
189
- var _a;
190
- this.messagesV2 = parseConfigure(signedData);
191
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] Protocol V2 schema configured');
192
- }
193
188
  listen() {
194
189
  }
195
190
  stop() {
@@ -199,7 +194,7 @@ class NodeUsbTransport {
199
194
  if (!this.messages) {
200
195
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
201
196
  }
202
- const encodeBuffers = ProtocolV1.encodeMessageChunks(this.messages, name, data);
197
+ const encodeBuffers = buildEncodeBuffers(this.messages, name, data);
203
198
  yield this.sendAllChunksWithRetry(path, encodeBuffers);
204
199
  });
205
200
  }
@@ -213,7 +208,7 @@ class NodeUsbTransport {
213
208
  if (!this.messages) {
214
209
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
215
210
  }
216
- return ProtocolV1.decodeMessage(this.messages, resData);
211
+ return receiveOne(this.messages, resData);
217
212
  });
218
213
  }
219
214
  enumerate() {
@@ -243,31 +238,20 @@ class NodeUsbTransport {
243
238
  }
244
239
  acquire(input) {
245
240
  var _a, _b, _c;
246
- return __awaiter(this, void 0, void 0, function* () {
247
- this.cancelled = false;
248
- const path = (_a = input.path) !== null && _a !== void 0 ? _a : '';
249
- if (!path) {
250
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, 'No device path provided');
251
- }
252
- try {
253
- this.openDevice(path);
254
- yield this.detectProtocol(path, input.expectedProtocol);
255
- return path;
256
- }
257
- catch (error) {
258
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport acquire error: ', error);
259
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, (_c = error.message) !== null && _c !== void 0 ? _c : String(error));
260
- }
261
- });
241
+ const path = (_a = input.path) !== null && _a !== void 0 ? _a : '';
242
+ if (!path) {
243
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, 'No device path provided');
244
+ }
245
+ try {
246
+ this.openDevice(path);
247
+ return Promise.resolve(path);
248
+ }
249
+ catch (error) {
250
+ (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport acquire error: ', error);
251
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, (_c = error.message) !== null && _c !== void 0 ? _c : String(error));
252
+ }
262
253
  }
263
254
  release(path, _onclose) {
264
- return __awaiter(this, void 0, void 0, function* () {
265
- yield this.closeOpenDevice(path);
266
- this.deviceProtocol.delete(path);
267
- this.protocolV2Assemblers.delete(path);
268
- });
269
- }
270
- closeOpenDevice(path) {
271
255
  return __awaiter(this, void 0, void 0, function* () {
272
256
  const openDev = this.openDevices.get(path);
273
257
  if (!openDev)
@@ -294,7 +278,7 @@ class NodeUsbTransport {
294
278
  this.openDevices.delete(path);
295
279
  });
296
280
  }
297
- call(path, name, data, options) {
281
+ call(path, name, data) {
298
282
  var _a, _b;
299
283
  return __awaiter(this, void 0, void 0, function* () {
300
284
  this.cancelled = false;
@@ -304,35 +288,20 @@ class NodeUsbTransport {
304
288
  if (!this.openDevices.get(path)) {
305
289
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device not acquired: ${path}`);
306
290
  }
307
- const protocol = this.deviceProtocol.get(path);
308
- if (!protocol) {
309
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
310
- }
291
+ const { messages } = this;
311
292
  if (transport.LogBlockCommand.has(name)) {
312
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('NodeUsbTransport call-', ' name: ', name, ' protocol: ', protocol);
293
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('NodeUsbTransport call-', ' name: ', name);
313
294
  }
314
295
  else {
315
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
316
- }
317
- if (protocol === 'V2') {
318
- return this.callProtocolV2(path, name, data, options);
319
- }
320
- return this.callProtocolV1(path, name, data, options);
321
- });
322
- }
323
- callProtocolV1(path, name, data, options) {
324
- return __awaiter(this, void 0, void 0, function* () {
325
- const { messages } = this;
326
- if (!messages) {
327
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
296
+ (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data);
328
297
  }
329
- const encodeBuffers = ProtocolV1.encodeMessageChunks(messages, name, data);
298
+ const encodeBuffers = buildEncodeBuffers(messages, name, data);
330
299
  yield this.sendAllChunksWithRetry(path, encodeBuffers);
331
- const resData = yield this.receiveData(path, this.getOpenDevice(path), options === null || options === void 0 ? void 0 : options.timeoutMs);
300
+ const resData = yield this.receiveData(path, this.getOpenDevice(path));
332
301
  if (typeof resData !== 'string') {
333
302
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, 'Returning data is not string.');
334
303
  }
335
- const jsonData = ProtocolV1.decodeMessage(messages, resData);
304
+ const jsonData = receiveOne(messages, resData);
336
305
  return check.call(jsonData);
337
306
  });
338
307
  }
@@ -372,17 +341,6 @@ class NodeUsbTransport {
372
341
  message.includes('timeout') ||
373
342
  message.includes('interrupt'));
374
343
  }
375
- getDeviceInterface(dev) {
376
- var _a;
377
- const { interfaces } = dev;
378
- if (!(interfaces === null || interfaces === void 0 ? void 0 : interfaces.length)) {
379
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, 'USB interface not found');
380
- }
381
- const vendorInterface = interfaces.find(iface => iface.descriptor.bInterfaceClass === 0xff);
382
- const defaultInterface = interfaces.find(iface => iface.descriptor.bInterfaceNumber === INTERFACE_NUMBER);
383
- const iface = (_a = vendorInterface !== null && vendorInterface !== void 0 ? vendorInterface : defaultInterface) !== null && _a !== void 0 ? _a : interfaces[0];
384
- return iface;
385
- }
386
344
  reconnectForRetry(path, direction, attempt, error) {
387
345
  const existing = this.reconnectLocks.get(path);
388
346
  if (existing)
@@ -392,7 +350,7 @@ class NodeUsbTransport {
392
350
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
393
351
  yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
394
352
  try {
395
- yield this.closeOpenDevice(path);
353
+ yield this.release(path);
396
354
  }
397
355
  catch (releaseError) {
398
356
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[NodeUsbTransport] release before retry error:', releaseError);
@@ -479,7 +437,7 @@ class NodeUsbTransport {
479
437
  });
480
438
  }
481
439
  openDevice(path) {
482
- var _a, _b, _c;
440
+ var _a;
483
441
  const existing = this.openDevices.get(path);
484
442
  if (existing)
485
443
  return;
@@ -492,19 +450,19 @@ class NodeUsbTransport {
492
450
  dev.open();
493
451
  try {
494
452
  dev.timeout = TRANSFER_TIMEOUT_MS;
495
- const iface = this.getDeviceInterface(dev);
453
+ const iface = dev.interface(INTERFACE_NUMBER);
496
454
  if (process.platform === 'linux') {
497
455
  try {
498
456
  if (iface.isKernelDriverActive()) {
499
457
  iface.detachKernelDriver();
500
458
  }
501
459
  }
502
- catch (_d) {
460
+ catch (_b) {
503
461
  }
504
462
  }
505
463
  iface.claim();
506
- const epIn = (_b = iface.endpoints.find((e) => e.direction === 'in' && e.address === ENDPOINT_IN)) !== null && _b !== void 0 ? _b : iface.endpoints.find((e) => e.direction === 'in');
507
- const epOut = (_c = iface.endpoints.find((e) => e.direction === 'out' && e.address === ENDPOINT_OUT)) !== null && _c !== void 0 ? _c : iface.endpoints.find((e) => e.direction === 'out');
464
+ const epIn = iface.endpoints.find((e) => e.direction === 'in' && e.address === ENDPOINT_IN);
465
+ const epOut = iface.endpoints.find((e) => e.direction === 'out' && e.address === ENDPOINT_OUT);
508
466
  if (!epIn || !epOut) {
509
467
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, 'USB endpoints not found (expected IN 0x81, OUT 0x01)');
510
468
  }
@@ -516,232 +474,16 @@ class NodeUsbTransport {
516
474
  try {
517
475
  dev.close();
518
476
  }
519
- catch (_e) {
477
+ catch (_c) {
520
478
  }
521
479
  throw err;
522
480
  }
523
481
  }
524
- createProtocolMismatchError(expected) {
525
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
526
- }
527
- createProtocolDetectionError() {
528
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
529
- }
530
- detectProtocol(path, expectedProtocol) {
531
- var _a, _b, _c, _d, _e;
532
- return __awaiter(this, void 0, void 0, function* () {
533
- if (expectedProtocol === 'V1') {
534
- if (yield this.probeProtocolV1(path)) {
535
- this.deviceProtocol.set(path, 'V1');
536
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
537
- return 'V1';
538
- }
539
- throw this.createProtocolMismatchError(expectedProtocol);
540
- }
541
- if (expectedProtocol === 'V2') {
542
- if (yield this.probeProtocolV2(path)) {
543
- this.deviceProtocol.set(path, 'V2');
544
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
545
- return 'V2';
546
- }
547
- throw this.createProtocolMismatchError(expectedProtocol);
548
- }
549
- if (this.deviceProtocol.get(path) === 'V2' && (yield this.probeProtocolV2(path))) {
550
- this.deviceProtocol.set(path, 'V2');
551
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
552
- return 'V2';
553
- }
554
- if (yield this.probeProtocolV1(path)) {
555
- this.deviceProtocol.set(path, 'V1');
556
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1`);
557
- return 'V1';
558
- }
559
- if (yield this.probeProtocolV2(path)) {
560
- this.deviceProtocol.set(path, 'V2');
561
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2`);
562
- return 'V2';
563
- }
564
- this.deviceProtocol.delete(path);
565
- throw this.createProtocolDetectionError();
566
- });
567
- }
568
- resetConnectionAfterProbe(path) {
569
- var _a, _b;
482
+ receiveData(path, dev) {
570
483
  return __awaiter(this, void 0, void 0, function* () {
571
- (_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
572
- try {
573
- yield this.closeOpenDevice(path);
574
- }
575
- catch (error) {
576
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[NodeUsbTransport] close after protocol probe error:', error);
577
- }
578
- yield this.enumerate();
579
- this.openDevice(path);
580
- });
581
- }
582
- withProtocolReadTimeout(path, promise, timeoutMs, protocol) {
583
- return __awaiter(this, void 0, void 0, function* () {
584
- let timer;
585
- let timedOut = false;
586
- const waitForeverAfterTimeout = () => new Promise(() => { });
587
- const guardedPromise = promise.then(value => (timedOut ? waitForeverAfterTimeout() : value), error => {
588
- if (timedOut) {
589
- return waitForeverAfterTimeout();
590
- }
591
- throw error;
592
- });
593
- try {
594
- return yield Promise.race([
595
- guardedPromise,
596
- new Promise((_, reject) => {
597
- timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
598
- var _a;
599
- timedOut = true;
600
- try {
601
- yield this.resetConnectionAfterProbe(path);
602
- }
603
- catch (error) {
604
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] reset after Protocol ${protocol} timeout failed:`, error);
605
- }
606
- finally {
607
- reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
608
- }
609
- }), timeoutMs);
610
- }),
611
- ]);
612
- }
613
- finally {
614
- if (timer)
615
- clearTimeout(timer);
616
- }
617
- });
618
- }
619
- probeProtocolV1(path) {
620
- var _a;
621
- return __awaiter(this, void 0, void 0, function* () {
622
- if (!this.messages) {
623
- return false;
624
- }
625
- try {
626
- yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
627
- return true;
628
- }
629
- catch (error) {
630
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] Protocol V1 Initialize probe failed:', error);
631
- return false;
632
- }
633
- });
634
- }
635
- probeProtocolV2(path) {
636
- return __awaiter(this, void 0, void 0, function* () {
637
- if (!this.messages || !this.messagesV2) {
638
- return false;
639
- }
640
- return transport.probeProtocolV2({
641
- call: (name, data, options) => this.callProtocolV2(path, name, data, options),
642
- timeoutMs: PROTOCOL_PROBE_TIMEOUT,
643
- logger: this.Log,
644
- logPrefix: 'ProtocolV2 NodeUSB',
645
- onProbeFailed: () => this.resetConnectionAfterProbe(path),
646
- });
647
- });
648
- }
649
- writeProtocolV2Frame(path, frame) {
650
- var _a;
651
- return __awaiter(this, void 0, void 0, function* () {
652
- let lastError;
653
- for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt++) {
654
- if (this.cancelled) {
655
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
656
- }
657
- try {
658
- yield transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(frame));
659
- return;
660
- }
661
- catch (error) {
662
- lastError = error;
663
- const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryableError(error);
664
- if (!shouldRetry) {
665
- throw error;
666
- }
667
- try {
668
- yield this.reconnectForRetry(path, 'out', attempt, error);
669
- }
670
- catch (reconnectError) {
671
- lastError = reconnectError;
672
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] Protocol V2 write reconnect failed on retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(reconnectError)}`);
673
- break;
674
- }
675
- }
676
- }
677
- throw lastError;
678
- });
679
- }
680
- receiveProtocolV2Frame(path, timeoutMs) {
681
- return __awaiter(this, void 0, void 0, function* () {
682
- let assembler = this.protocolV2Assemblers.get(path);
683
- if (!assembler) {
684
- assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES);
685
- this.protocolV2Assemblers.set(path, assembler);
686
- }
687
- let frame = assembler.push(new Uint8Array(0));
688
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
689
- while (!frame) {
690
- const transferIn = this.transferInWithRetry(path, this.getOpenDevice(path), transport.PROTOCOL_V2_FRAME_MAX_BYTES);
691
- const packet = deadline
692
- ? yield this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V2')
693
- : yield transferIn;
694
- const bytes = new Uint8Array(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
695
- try {
696
- frame = assembler.push(bytes);
697
- }
698
- catch (error) {
699
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
700
- }
701
- }
702
- return frame;
703
- });
704
- }
705
- callProtocolV2(path, name, data, options) {
706
- var _a;
707
- return __awaiter(this, void 0, void 0, function* () {
708
- const protocolV1Messages = this.messages;
709
- if (!this.messagesV2) {
710
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured, 'Protocol V2 schema not configured');
711
- }
712
- if (!protocolV1Messages) {
713
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
714
- }
715
- const session = new transport.ProtocolV2Session({
716
- schemas: {
717
- protocolV1: protocolV1Messages,
718
- protocolV2: this.messagesV2,
719
- },
720
- router: transport.PROTOCOL_V2_CHANNEL_USB,
721
- writeFrame: (frame) => this.writeProtocolV2Frame(path, frame),
722
- readFrame: () => this.receiveProtocolV2Frame(path, options === null || options === void 0 ? void 0 : options.timeoutMs),
723
- logger: this.Log,
724
- logPrefix: 'ProtocolV2 NodeUSB',
725
- createTimeoutError: (_messageName, timeoutMs) => new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`),
726
- });
727
- (_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
728
- return session.call(name, data, options);
729
- });
730
- }
731
- receiveData(path, dev, timeoutMs) {
732
- return __awaiter(this, void 0, void 0, function* () {
733
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
734
- const readPacket = () => __awaiter(this, void 0, void 0, function* () {
735
- const transferIn = this.transferInWithRetry(path, this.getOpenDevice(path), PACKET_SIZE);
736
- return deadline
737
- ? this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V1')
738
- : transferIn;
739
- });
740
- const firstPacket = timeoutMs
741
- ? yield readPacket()
742
- : yield this.transferInWithRetry(path, dev, PACKET_SIZE);
484
+ const firstPacket = yield this.transferInWithRetry(path, dev, PACKET_SIZE);
743
485
  const firstData = skipReportByte(firstPacket);
744
- const { length, typeId, restBuffer } = ProtocolV1.decodeFirstChunk(toArrayBuffer(firstData));
486
+ const { length, typeId, restBuffer } = decodeProtocol.decodeChunked(toArrayBuffer(firstData));
745
487
  const lengthWithHeader = Number(length) + HEADER_LENGTH;
746
488
  const decoded = new ByteBuffer__default["default"](lengthWithHeader);
747
489
  decoded.writeUint16(typeId);
@@ -750,7 +492,7 @@ class NodeUsbTransport {
750
492
  decoded.append(restBuffer);
751
493
  }
752
494
  while (decoded.offset < lengthWithHeader) {
753
- const packet = yield readPacket();
495
+ const packet = yield this.transferInWithRetry(path, this.getOpenDevice(path), PACKET_SIZE);
754
496
  const pktData = skipReportByte(packet);
755
497
  const buf = toArrayBuffer(pktData);
756
498
  if (lengthWithHeader - decoded.offset >= PAYLOAD_SIZE) {
@@ -765,9 +507,7 @@ class NodeUsbTransport {
765
507
  return Buffer.from(result).toString('hex');
766
508
  });
767
509
  }
768
- getProtocolType(path) {
769
- return this.deviceProtocol.get(path);
770
- }
771
510
  }
772
511
 
773
- module.exports = NodeUsbTransport;
512
+ exports.PACKET_SIZE = PACKET_SIZE;
513
+ exports["default"] = NodeUsbTransport;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-usb",
3
- "version": "1.1.27-alpha.41",
3
+ "version": "1.1.27-alpha.6",
4
4
  "description": "OneKey hardware wallet direct USB transport plugin (libusb)",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -20,10 +20,10 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-shared": "1.1.27-alpha.41",
24
- "@onekeyfe/hd-transport": "1.1.27-alpha.41",
23
+ "@onekeyfe/hd-shared": "1.1.27-alpha.6",
24
+ "@onekeyfe/hd-transport": "1.1.27-alpha.6",
25
25
  "bytebuffer": "^5.0.1",
26
26
  "usb": "^2.14.0"
27
27
  },
28
- "gitHead": "09cece9f0c9d85f55ae2f7f0458ed7d8833483d2"
28
+ "gitHead": "a1fb7786313ccee4b1e570c53ae2c05a96f785df"
29
29
  }
@@ -0,0 +1,11 @@
1
+ /** USB packet size in bytes */
2
+ export const PACKET_SIZE = 64;
3
+
4
+ /** Protocol marker byte (0x3F = '?') — first byte of every packet */
5
+ export const REPORT_ID = 0x3f;
6
+
7
+ /** Usable payload per packet after stripping the 0x3F report byte */
8
+ export const PAYLOAD_SIZE = PACKET_SIZE - 1;
9
+
10
+ /** Protocol header length: typeId (2 bytes) + length (4 bytes) */
11
+ export const HEADER_LENGTH = 6;
package/src/index.ts CHANGED
@@ -1,33 +1,14 @@
1
1
  import ByteBuffer from 'bytebuffer';
2
2
  import * as usb from 'usb';
3
- import transport, {
4
- LogBlockCommand,
5
- PROTOCOL_V1_CHUNK_PAYLOAD_SIZE,
6
- PROTOCOL_V1_MESSAGE_HEADER_SIZE,
7
- PROTOCOL_V1_REPORT_ID,
8
- PROTOCOL_V1_USB_PACKET_SIZE,
9
- PROTOCOL_V2_CHANNEL_USB,
10
- PROTOCOL_V2_FRAME_MAX_BYTES,
11
- ProtocolV2FrameAssembler,
12
- ProtocolV2Session,
13
- probeProtocolV2 as probeProtocolV2Helper,
14
- } from '@onekeyfe/hd-transport';
3
+ import transport, { LogBlockCommand } from '@onekeyfe/hd-transport';
15
4
  import { ERRORS, HardwareErrorCode, ONEKEY_WEBUSB_FILTER, wait } from '@onekeyfe/hd-shared';
16
5
 
17
- import type EventEmitter from 'events';
18
- import type {
19
- AcquireInput,
20
- OneKeyDeviceInfo,
21
- ProtocolType,
22
- TransportCallOptions,
23
- } from '@onekeyfe/hd-transport';
6
+ import { HEADER_LENGTH, PACKET_SIZE, PAYLOAD_SIZE, REPORT_ID } from './constants';
24
7
 
25
- const { parseConfigure, ProtocolV1, check } = transport;
8
+ import type EventEmitter from 'events';
9
+ import type { AcquireInput, OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
26
10
 
27
- const PACKET_SIZE = PROTOCOL_V1_USB_PACKET_SIZE;
28
- const REPORT_ID = PROTOCOL_V1_REPORT_ID;
29
- const PAYLOAD_SIZE = PROTOCOL_V1_CHUNK_PAYLOAD_SIZE;
30
- const HEADER_LENGTH = PROTOCOL_V1_MESSAGE_HEADER_SIZE;
11
+ const { parseConfigure, buildEncodeBuffers, decodeProtocol, receiveOne, check } = transport;
31
12
 
32
13
  /** USB interface number for vendor-specific communication */
33
14
  const INTERFACE_NUMBER = 0;
@@ -44,7 +25,6 @@ const SERIAL_READ_TIMEOUT_MS = 5000;
44
25
  /** Packet I/O retry configuration (matches WebUsbTransport) */
45
26
  const PACKET_IO_MAX_RETRIES = 3;
46
27
  const PACKET_IO_RETRY_DELAY = 300;
47
- const PROTOCOL_PROBE_TIMEOUT = 1000;
48
28
 
49
29
  /**
50
30
  * Opened device state — holds the USB device, claimed interface, and endpoints.
@@ -183,9 +163,6 @@ function toArrayBuffer(buf: Buffer): ArrayBuffer {
183
163
  export default class NodeUsbTransport {
184
164
  messages: ReturnType<typeof transport.parseConfigure> | undefined;
185
165
 
186
- /** Protobuf schema for Protocol V2 transports. */
187
- messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
188
-
189
166
  name = 'NodeUsbTransport';
190
167
 
191
168
  version = '';
@@ -204,12 +181,6 @@ export default class NodeUsbTransport {
204
181
  /** path → opened device state */
205
182
  private openDevices = new Map<string, OpenDevice>();
206
183
 
207
- /** Per-path protocol type detected by active wire-level probe. */
208
- private deviceProtocol: Map<string, ProtocolType> = new Map();
209
-
210
- /** Per-path Protocol V2 frame assembler, preserving buffered frames during reads. */
211
- private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
212
-
213
184
  /** per-path reconnect lock to prevent concurrent reconnects */
214
185
  private reconnectLocks = new Map<string, Promise<OpenDevice>>();
215
186
 
@@ -233,11 +204,6 @@ export default class NodeUsbTransport {
233
204
  return Promise.resolve();
234
205
  }
235
206
 
236
- configureProtocolV2(signedData: any) {
237
- this.messagesV2 = parseConfigure(signedData);
238
- this.Log?.debug('[NodeUsbTransport] Protocol V2 schema configured');
239
- }
240
-
241
207
  listen() {
242
208
  // empty — could add hotplug events via usb.on('attach'/'detach')
243
209
  }
@@ -254,7 +220,7 @@ export default class NodeUsbTransport {
254
220
  if (!this.messages) {
255
221
  throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
256
222
  }
257
- const encodeBuffers = ProtocolV1.encodeMessageChunks(this.messages, name, data);
223
+ const encodeBuffers = buildEncodeBuffers(this.messages, name, data);
258
224
  await this.sendAllChunksWithRetry(path, encodeBuffers);
259
225
  }
260
226
 
@@ -271,7 +237,7 @@ export default class NodeUsbTransport {
271
237
  if (!this.messages) {
272
238
  throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
273
239
  }
274
- return ProtocolV1.decodeMessage(this.messages, resData);
240
+ return receiveOne(this.messages, resData);
275
241
  }
276
242
 
277
243
  /**
@@ -309,9 +275,7 @@ export default class NodeUsbTransport {
309
275
  /**
310
276
  * Acquire device — open USB device, claim interface, return path (string).
311
277
  */
312
- async acquire(input: AcquireInput): Promise<string> {
313
- this.cancelled = false;
314
-
278
+ acquire(input: AcquireInput): Promise<string> {
315
279
  const path = input.path ?? '';
316
280
  if (!path) {
317
281
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, 'No device path provided');
@@ -319,8 +283,7 @@ export default class NodeUsbTransport {
319
283
 
320
284
  try {
321
285
  this.openDevice(path);
322
- await this.detectProtocol(path, input.expectedProtocol);
323
- return path;
286
+ return Promise.resolve(path);
324
287
  } catch (error: any) {
325
288
  this.Log?.debug('NodeUsbTransport acquire error: ', error);
326
289
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, error.message ?? String(error));
@@ -331,12 +294,6 @@ export default class NodeUsbTransport {
331
294
  * Release device — release interface and close.
332
295
  */
333
296
  async release(path: string, _onclose?: boolean): Promise<void> {
334
- await this.closeOpenDevice(path);
335
- this.deviceProtocol.delete(path);
336
- this.protocolV2Assemblers.delete(path);
337
- }
338
-
339
- private async closeOpenDevice(path: string): Promise<void> {
340
297
  const openDev = this.openDevices.get(path);
341
298
  if (!openDev) return;
342
299
 
@@ -365,12 +322,7 @@ export default class NodeUsbTransport {
365
322
  * Call device method — encode protobuf, send packets, receive response.
366
323
  * This is the core method that replaces LowlevelTransport's call + UsbPlugin's send/receive.
367
324
  */
368
- async call(
369
- path: string,
370
- name: string,
371
- data: Record<string, unknown>,
372
- options?: TransportCallOptions
373
- ) {
325
+ async call(path: string, name: string, data: Record<string, unknown>) {
374
326
  this.cancelled = false;
375
327
 
376
328
  if (!this.messages) {
@@ -381,57 +333,26 @@ export default class NodeUsbTransport {
381
333
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, `Device not acquired: ${path}`);
382
334
  }
383
335
 
384
- const protocol = this.deviceProtocol.get(path);
385
- if (!protocol) {
386
- throw ERRORS.TypedError(
387
- HardwareErrorCode.RuntimeError,
388
- `Device protocol has not been detected for ${path}`
389
- );
390
- }
336
+ const { messages } = this;
391
337
  if (LogBlockCommand.has(name)) {
392
- this.Log?.debug('NodeUsbTransport call-', ' name: ', name, ' protocol: ', protocol);
338
+ this.Log?.debug('NodeUsbTransport call-', ' name: ', name);
393
339
  } else {
394
- this.Log?.debug(
395
- 'NodeUsbTransport call-',
396
- ' name: ',
397
- name,
398
- ' data: ',
399
- data,
400
- ' protocol: ',
401
- protocol
402
- );
340
+ this.Log?.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data);
403
341
  }
404
342
 
405
- if (protocol === 'V2') {
406
- return this.callProtocolV2(path, name, data, options);
407
- }
408
-
409
- return this.callProtocolV1(path, name, data, options);
410
- }
411
-
412
- private async callProtocolV1(
413
- path: string,
414
- name: string,
415
- data: Record<string, unknown>,
416
- options?: TransportCallOptions
417
- ) {
418
- const { messages } = this;
419
- if (!messages) {
420
- throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
421
- }
422
343
  // Encode protobuf message into 63-byte chunks (same as WebUsbTransport)
423
- const encodeBuffers = ProtocolV1.encodeMessageChunks(messages, name, data);
344
+ const encodeBuffers = buildEncodeBuffers(messages, name, data);
424
345
 
425
346
  // Send all chunks with retry — if any chunk fails and reconnects,
426
347
  // restart the entire send sequence from chunk 0 (device resets state on reconnect)
427
348
  await this.sendAllChunksWithRetry(path, encodeBuffers);
428
349
 
429
350
  // Receive response — re-resolve in case reconnect happened during send
430
- const resData = await this.receiveData(path, this.getOpenDevice(path), options?.timeoutMs);
351
+ const resData = await this.receiveData(path, this.getOpenDevice(path));
431
352
  if (typeof resData !== 'string') {
432
353
  throw ERRORS.TypedError(HardwareErrorCode.NetworkError, 'Returning data is not string.');
433
354
  }
434
- const jsonData = ProtocolV1.decodeMessage(messages, resData);
355
+ const jsonData = receiveOne(messages, resData);
435
356
  return check.call(jsonData);
436
357
  }
437
358
 
@@ -480,21 +401,6 @@ export default class NodeUsbTransport {
480
401
  );
481
402
  }
482
403
 
483
- private getDeviceInterface(dev: usb.Device): usb.Interface {
484
- const { interfaces } = dev;
485
- if (!interfaces?.length) {
486
- throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, 'USB interface not found');
487
- }
488
-
489
- const vendorInterface = interfaces.find(iface => iface.descriptor.bInterfaceClass === 0xff);
490
- const defaultInterface = interfaces.find(
491
- iface => iface.descriptor.bInterfaceNumber === INTERFACE_NUMBER
492
- );
493
- const iface = vendorInterface ?? defaultInterface ?? interfaces[0];
494
-
495
- return iface;
496
- }
497
-
498
404
  /**
499
405
  * Reconnect device before retrying a failed transfer (aligned with WebUsbTransport).
500
406
  * Uses per-path lock to prevent concurrent reconnects to the same device.
@@ -517,9 +423,9 @@ export default class NodeUsbTransport {
517
423
  );
518
424
  await wait(attempt * PACKET_IO_RETRY_DELAY);
519
425
 
520
- // Close the existing device without clearing the detected protocol cache.
426
+ // Close the existing device
521
427
  try {
522
- await this.closeOpenDevice(path);
428
+ await this.release(path);
523
429
  } catch (releaseError) {
524
430
  this.Log?.debug('[NodeUsbTransport] release before retry error:', releaseError);
525
431
  }
@@ -646,7 +552,7 @@ export default class NodeUsbTransport {
646
552
  try {
647
553
  dev.timeout = TRANSFER_TIMEOUT_MS;
648
554
 
649
- const iface = this.getDeviceInterface(dev);
555
+ const iface = dev.interface(INTERFACE_NUMBER);
650
556
 
651
557
  // On Linux, detach kernel driver if active
652
558
  if (process.platform === 'linux') {
@@ -661,14 +567,12 @@ export default class NodeUsbTransport {
661
567
 
662
568
  iface.claim();
663
569
 
664
- const epIn =
665
- iface.endpoints.find(
666
- (e): e is usb.InEndpoint => e.direction === 'in' && e.address === ENDPOINT_IN
667
- ) ?? iface.endpoints.find((e): e is usb.InEndpoint => e.direction === 'in');
668
- const epOut =
669
- iface.endpoints.find(
670
- (e): e is usb.OutEndpoint => e.direction === 'out' && e.address === ENDPOINT_OUT
671
- ) ?? iface.endpoints.find((e): e is usb.OutEndpoint => e.direction === 'out');
570
+ const epIn = iface.endpoints.find(
571
+ (e): e is usb.InEndpoint => e.direction === 'in' && e.address === ENDPOINT_IN
572
+ );
573
+ const epOut = iface.endpoints.find(
574
+ (e): e is usb.OutEndpoint => e.direction === 'out' && e.address === ENDPOINT_OUT
575
+ );
672
576
 
673
577
  if (!epIn || !epOut) {
674
578
  throw ERRORS.TypedError(
@@ -691,273 +595,17 @@ export default class NodeUsbTransport {
691
595
  }
692
596
  }
693
597
 
694
- private createProtocolMismatchError(expected: ProtocolType) {
695
- return ERRORS.TypedError(
696
- HardwareErrorCode.RuntimeError,
697
- `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`
698
- );
699
- }
700
-
701
- private createProtocolDetectionError() {
702
- return ERRORS.TypedError(
703
- HardwareErrorCode.RuntimeError,
704
- 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping'
705
- );
706
- }
707
-
708
- private async detectProtocol(
709
- path: string,
710
- expectedProtocol?: ProtocolType
711
- ): Promise<ProtocolType> {
712
- if (expectedProtocol === 'V1') {
713
- if (await this.probeProtocolV1(path)) {
714
- this.deviceProtocol.set(path, 'V1');
715
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
716
- return 'V1';
717
- }
718
- throw this.createProtocolMismatchError(expectedProtocol);
719
- }
720
-
721
- if (expectedProtocol === 'V2') {
722
- if (await this.probeProtocolV2(path)) {
723
- this.deviceProtocol.set(path, 'V2');
724
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
725
- return 'V2';
726
- }
727
- throw this.createProtocolMismatchError(expectedProtocol);
728
- }
729
-
730
- if (this.deviceProtocol.get(path) === 'V2' && (await this.probeProtocolV2(path))) {
731
- this.deviceProtocol.set(path, 'V2');
732
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
733
- return 'V2';
734
- }
735
-
736
- if (await this.probeProtocolV1(path)) {
737
- this.deviceProtocol.set(path, 'V1');
738
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1`);
739
- return 'V1';
740
- }
741
-
742
- if (await this.probeProtocolV2(path)) {
743
- this.deviceProtocol.set(path, 'V2');
744
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2`);
745
- return 'V2';
746
- }
747
-
748
- this.deviceProtocol.delete(path);
749
- throw this.createProtocolDetectionError();
750
- }
751
-
752
- private async resetConnectionAfterProbe(path: string) {
753
- this.protocolV2Assemblers.get(path)?.reset();
754
-
755
- try {
756
- await this.closeOpenDevice(path);
757
- } catch (error) {
758
- this.Log?.debug('[NodeUsbTransport] close after protocol probe error:', error);
759
- }
760
-
761
- await this.enumerate();
762
- this.openDevice(path);
763
- }
764
-
765
- private async withProtocolReadTimeout<T>(
766
- path: string,
767
- promise: Promise<T>,
768
- timeoutMs: number,
769
- protocol: ProtocolType
770
- ): Promise<T> {
771
- let timer: ReturnType<typeof setTimeout> | undefined;
772
- let timedOut = false;
773
- const waitForeverAfterTimeout = () => new Promise<never>(() => {});
774
- const guardedPromise = promise.then(
775
- value => (timedOut ? waitForeverAfterTimeout() : value),
776
- error => {
777
- if (timedOut) {
778
- return waitForeverAfterTimeout();
779
- }
780
- throw error;
781
- }
782
- );
783
- try {
784
- return await Promise.race([
785
- guardedPromise,
786
- new Promise<never>((_, reject) => {
787
- timer = setTimeout(async () => {
788
- timedOut = true;
789
- try {
790
- await this.resetConnectionAfterProbe(path);
791
- } catch (error) {
792
- this.Log?.debug(
793
- `[NodeUsbTransport] reset after Protocol ${protocol} timeout failed:`,
794
- error
795
- );
796
- } finally {
797
- reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
798
- }
799
- }, timeoutMs);
800
- }),
801
- ]);
802
- } finally {
803
- if (timer) clearTimeout(timer);
804
- }
805
- }
806
-
807
- private async probeProtocolV1(path: string) {
808
- if (!this.messages) {
809
- return false;
810
- }
811
-
812
- try {
813
- await this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
814
- return true;
815
- } catch (error) {
816
- this.Log?.debug('[NodeUsbTransport] Protocol V1 Initialize probe failed:', error);
817
- return false;
818
- }
819
- }
820
-
821
- private async probeProtocolV2(path: string) {
822
- if (!this.messages || !this.messagesV2) {
823
- return false;
824
- }
825
-
826
- return probeProtocolV2Helper({
827
- call: (name, data, options) => this.callProtocolV2(path, name, data, options),
828
- timeoutMs: PROTOCOL_PROBE_TIMEOUT,
829
- logger: this.Log,
830
- logPrefix: 'ProtocolV2 NodeUSB',
831
- onProbeFailed: () => this.resetConnectionAfterProbe(path),
832
- });
833
- }
834
-
835
- private async writeProtocolV2Frame(path: string, frame: Uint8Array) {
836
- let lastError: unknown;
837
- for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt++) {
838
- if (this.cancelled) {
839
- throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
840
- }
841
- try {
842
- await transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(frame));
843
- return;
844
- } catch (error) {
845
- lastError = error;
846
- const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryableError(error);
847
- if (!shouldRetry) {
848
- throw error;
849
- }
850
- try {
851
- await this.reconnectForRetry(path, 'out', attempt, error);
852
- } catch (reconnectError) {
853
- lastError = reconnectError;
854
- this.Log?.debug(
855
- `[NodeUsbTransport] Protocol V2 write reconnect failed on retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(
856
- reconnectError
857
- )}`
858
- );
859
- break;
860
- }
861
- }
862
- }
863
- throw lastError;
864
- }
865
-
866
- private async receiveProtocolV2Frame(path: string, timeoutMs?: number): Promise<Uint8Array> {
867
- let assembler = this.protocolV2Assemblers.get(path);
868
- if (!assembler) {
869
- assembler = new ProtocolV2FrameAssembler(PROTOCOL_V2_FRAME_MAX_BYTES);
870
- this.protocolV2Assemblers.set(path, assembler);
871
- }
872
-
873
- let frame: Uint8Array | undefined = assembler.push(new Uint8Array(0));
874
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
875
-
876
- while (!frame) {
877
- const transferIn = this.transferInWithRetry(
878
- path,
879
- this.getOpenDevice(path),
880
- PROTOCOL_V2_FRAME_MAX_BYTES
881
- );
882
- const packet = deadline
883
- ? await this.withProtocolReadTimeout(
884
- path,
885
- transferIn,
886
- Math.max(deadline - Date.now(), 1),
887
- 'V2'
888
- )
889
- : await transferIn;
890
- const bytes = new Uint8Array(
891
- packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength)
892
- );
893
- try {
894
- frame = assembler.push(bytes);
895
- } catch (error) {
896
- throw ERRORS.TypedError(
897
- HardwareErrorCode.NetworkError,
898
- error instanceof Error ? error.message : String(error)
899
- );
900
- }
901
- }
902
- return frame;
903
- }
904
-
905
- private async callProtocolV2(
906
- path: string,
907
- name: string,
908
- data: Record<string, unknown>,
909
- options?: TransportCallOptions
910
- ) {
911
- const protocolV1Messages = this.messages;
912
- if (!this.messagesV2) {
913
- throw ERRORS.TypedError(
914
- HardwareErrorCode.TransportNotConfigured,
915
- 'Protocol V2 schema not configured'
916
- );
917
- }
918
- if (!protocolV1Messages) {
919
- throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
920
- }
921
-
922
- const session = new ProtocolV2Session({
923
- schemas: {
924
- protocolV1: protocolV1Messages,
925
- protocolV2: this.messagesV2,
926
- },
927
- router: PROTOCOL_V2_CHANNEL_USB,
928
- writeFrame: (frame: Uint8Array) => this.writeProtocolV2Frame(path, frame),
929
- readFrame: () => this.receiveProtocolV2Frame(path, options?.timeoutMs),
930
- logger: this.Log,
931
- logPrefix: 'ProtocolV2 NodeUSB',
932
- createTimeoutError: (_messageName: string, timeoutMs: number) =>
933
- new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`),
934
- });
935
-
936
- this.protocolV2Assemblers.get(path)?.reset();
937
- return session.call(name, data, options);
938
- }
939
-
940
598
  /**
941
599
  * Receive a complete protobuf response from the device.
942
600
  * Reads 64-byte packets, strips 0x3F marker, reassembles into hex string.
943
601
  */
944
- private async receiveData(path: string, dev: OpenDevice, timeoutMs?: number): Promise<string> {
945
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
946
- const readPacket = async () => {
947
- const transferIn = this.transferInWithRetry(path, this.getOpenDevice(path), PACKET_SIZE);
948
- return deadline
949
- ? this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V1')
950
- : transferIn;
951
- };
952
-
602
+ private async receiveData(path: string, dev: OpenDevice): Promise<string> {
953
603
  // Read first packet, skip report byte
954
- const firstPacket = timeoutMs
955
- ? await readPacket()
956
- : await this.transferInWithRetry(path, dev, PACKET_SIZE);
604
+ const firstPacket = await this.transferInWithRetry(path, dev, PACKET_SIZE);
957
605
  const firstData = skipReportByte(firstPacket);
958
606
 
959
607
  // Decode header: ## marker → { typeId, length, restBuffer }
960
- const { length, typeId, restBuffer } = ProtocolV1.decodeFirstChunk(toArrayBuffer(firstData));
608
+ const { length, typeId, restBuffer } = decodeProtocol.decodeChunked(toArrayBuffer(firstData));
961
609
 
962
610
  // Allocate result: typeId(2) + length(4) + payload(length)
963
611
  const lengthWithHeader = Number(length) + HEADER_LENGTH;
@@ -971,7 +619,7 @@ export default class NodeUsbTransport {
971
619
  // Read subsequent packets until complete
972
620
  // Re-resolve device on each iteration so we use a fresh handle after any reconnect
973
621
  while (decoded.offset < lengthWithHeader) {
974
- const packet = await readPacket();
622
+ const packet = await this.transferInWithRetry(path, this.getOpenDevice(path), PACKET_SIZE);
975
623
  const pktData = skipReportByte(packet);
976
624
  const buf = toArrayBuffer(pktData);
977
625
  if (lengthWithHeader - decoded.offset >= PAYLOAD_SIZE) {
@@ -985,8 +633,6 @@ export default class NodeUsbTransport {
985
633
  const result = decoded.toBuffer();
986
634
  return Buffer.from(result as unknown as ArrayBuffer).toString('hex');
987
635
  }
988
-
989
- getProtocolType(path: string): ProtocolType | undefined {
990
- return this.deviceProtocol.get(path);
991
- }
992
636
  }
637
+
638
+ export { PACKET_SIZE } from './constants';