@onekeyfe/hd-transport-usb 1.1.27-alpha.34 → 1.1.27-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,29 +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 detectProtocol;
49
- private resetConnectionAfterProbe;
50
- private withProtocolReadTimeout;
51
- private probeProtocolV1;
52
- private probeProtocolV2;
53
- private writeProtocolV2Frame;
54
- private receiveProtocolV2Frame;
55
- private callProtocolV2;
56
42
  private receiveData;
57
- getProtocolType(path: string): ProtocolType;
58
43
  }
59
44
 
60
- 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;YAkClB,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;YAOrB,cAAc;YAqCd,yBAAyB;YAazB,uBAAuB;YA0CvB,eAAe;YAcf,eAAe;YAcf,oBAAoB;YA+BpB,sBAAsB;YAuCtB,cAAc;YAuCd,WAAW;IA6CzB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
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,8 +278,8 @@ class NodeUsbTransport {
294
278
  this.openDevices.delete(path);
295
279
  });
296
280
  }
297
- call(path, name, data, options) {
298
- var _a, _b, _c;
281
+ call(path, name, data) {
282
+ var _a, _b;
299
283
  return __awaiter(this, void 0, void 0, function* () {
300
284
  this.cancelled = false;
301
285
  if (!this.messages) {
@@ -304,32 +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 = (_a = this.deviceProtocol.get(path)) !== null && _a !== void 0 ? _a : 'V1';
291
+ const { messages } = this;
308
292
  if (transport.LogBlockCommand.has(name)) {
309
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport call-', ' name: ', name, ' protocol: ', protocol);
293
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('NodeUsbTransport call-', ' name: ', name);
310
294
  }
311
295
  else {
312
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
313
- }
314
- if (protocol === 'V2') {
315
- return this.callProtocolV2(path, name, data, options);
316
- }
317
- return this.callProtocolV1(path, name, data, options);
318
- });
319
- }
320
- callProtocolV1(path, name, data, options) {
321
- return __awaiter(this, void 0, void 0, function* () {
322
- const { messages } = this;
323
- if (!messages) {
324
- 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);
325
297
  }
326
- const encodeBuffers = ProtocolV1.encodeMessageChunks(messages, name, data);
298
+ const encodeBuffers = buildEncodeBuffers(messages, name, data);
327
299
  yield this.sendAllChunksWithRetry(path, encodeBuffers);
328
- 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));
329
301
  if (typeof resData !== 'string') {
330
302
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, 'Returning data is not string.');
331
303
  }
332
- const jsonData = ProtocolV1.decodeMessage(messages, resData);
304
+ const jsonData = receiveOne(messages, resData);
333
305
  return check.call(jsonData);
334
306
  });
335
307
  }
@@ -369,17 +341,6 @@ class NodeUsbTransport {
369
341
  message.includes('timeout') ||
370
342
  message.includes('interrupt'));
371
343
  }
372
- getDeviceInterface(dev) {
373
- var _a;
374
- const { interfaces } = dev;
375
- if (!(interfaces === null || interfaces === void 0 ? void 0 : interfaces.length)) {
376
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, 'USB interface not found');
377
- }
378
- const vendorInterface = interfaces.find(iface => iface.descriptor.bInterfaceClass === 0xff);
379
- const defaultInterface = interfaces.find(iface => iface.descriptor.bInterfaceNumber === INTERFACE_NUMBER);
380
- const iface = (_a = vendorInterface !== null && vendorInterface !== void 0 ? vendorInterface : defaultInterface) !== null && _a !== void 0 ? _a : interfaces[0];
381
- return iface;
382
- }
383
344
  reconnectForRetry(path, direction, attempt, error) {
384
345
  const existing = this.reconnectLocks.get(path);
385
346
  if (existing)
@@ -389,7 +350,7 @@ class NodeUsbTransport {
389
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)}`);
390
351
  yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
391
352
  try {
392
- yield this.closeOpenDevice(path);
353
+ yield this.release(path);
393
354
  }
394
355
  catch (releaseError) {
395
356
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[NodeUsbTransport] release before retry error:', releaseError);
@@ -476,7 +437,7 @@ class NodeUsbTransport {
476
437
  });
477
438
  }
478
439
  openDevice(path) {
479
- var _a, _b, _c;
440
+ var _a;
480
441
  const existing = this.openDevices.get(path);
481
442
  if (existing)
482
443
  return;
@@ -489,19 +450,19 @@ class NodeUsbTransport {
489
450
  dev.open();
490
451
  try {
491
452
  dev.timeout = TRANSFER_TIMEOUT_MS;
492
- const iface = this.getDeviceInterface(dev);
453
+ const iface = dev.interface(INTERFACE_NUMBER);
493
454
  if (process.platform === 'linux') {
494
455
  try {
495
456
  if (iface.isKernelDriverActive()) {
496
457
  iface.detachKernelDriver();
497
458
  }
498
459
  }
499
- catch (_d) {
460
+ catch (_b) {
500
461
  }
501
462
  }
502
463
  iface.claim();
503
- 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');
504
- 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);
505
466
  if (!epIn || !epOut) {
506
467
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, 'USB endpoints not found (expected IN 0x81, OUT 0x01)');
507
468
  }
@@ -513,224 +474,16 @@ class NodeUsbTransport {
513
474
  try {
514
475
  dev.close();
515
476
  }
516
- catch (_e) {
477
+ catch (_c) {
517
478
  }
518
479
  throw err;
519
480
  }
520
481
  }
521
- createProtocolMismatchError(expected) {
522
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
523
- }
524
- detectProtocol(path, expectedProtocol) {
525
- var _a, _b, _c, _d;
526
- return __awaiter(this, void 0, void 0, function* () {
527
- if (expectedProtocol === 'V1') {
528
- if (yield this.probeProtocolV1(path)) {
529
- this.deviceProtocol.set(path, 'V1');
530
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
531
- return 'V1';
532
- }
533
- throw this.createProtocolMismatchError(expectedProtocol);
534
- }
535
- if (expectedProtocol === 'V2') {
536
- if (yield this.probeProtocolV2(path)) {
537
- this.deviceProtocol.set(path, 'V2');
538
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
539
- return 'V2';
540
- }
541
- throw this.createProtocolMismatchError(expectedProtocol);
542
- }
543
- if (this.deviceProtocol.get(path) === 'V2' && (yield this.probeProtocolV2(path))) {
544
- this.deviceProtocol.set(path, 'V2');
545
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
546
- return 'V2';
547
- }
548
- let protocol = 'V1';
549
- if (!(yield this.probeProtocolV1(path)) && (yield this.probeProtocolV2(path))) {
550
- protocol = 'V2';
551
- }
552
- this.deviceProtocol.set(path, protocol);
553
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> ${protocol}`);
554
- return protocol;
555
- });
556
- }
557
- resetConnectionAfterProbe(path) {
558
- var _a, _b;
482
+ receiveData(path, dev) {
559
483
  return __awaiter(this, void 0, void 0, function* () {
560
- (_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
561
- try {
562
- yield this.closeOpenDevice(path);
563
- }
564
- catch (error) {
565
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[NodeUsbTransport] close after protocol probe error:', error);
566
- }
567
- yield this.enumerate();
568
- this.openDevice(path);
569
- });
570
- }
571
- withProtocolReadTimeout(path, promise, timeoutMs, protocol) {
572
- return __awaiter(this, void 0, void 0, function* () {
573
- let timer;
574
- let timedOut = false;
575
- const waitForeverAfterTimeout = () => new Promise(() => { });
576
- const guardedPromise = promise.then(value => (timedOut ? waitForeverAfterTimeout() : value), error => {
577
- if (timedOut) {
578
- return waitForeverAfterTimeout();
579
- }
580
- throw error;
581
- });
582
- try {
583
- return yield Promise.race([
584
- guardedPromise,
585
- new Promise((_, reject) => {
586
- timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
587
- var _a;
588
- timedOut = true;
589
- try {
590
- yield this.resetConnectionAfterProbe(path);
591
- }
592
- catch (error) {
593
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] reset after Protocol ${protocol} timeout failed:`, error);
594
- }
595
- finally {
596
- reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
597
- }
598
- }), timeoutMs);
599
- }),
600
- ]);
601
- }
602
- finally {
603
- if (timer)
604
- clearTimeout(timer);
605
- }
606
- });
607
- }
608
- probeProtocolV1(path) {
609
- var _a;
610
- return __awaiter(this, void 0, void 0, function* () {
611
- if (!this.messages) {
612
- return false;
613
- }
614
- try {
615
- yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
616
- return true;
617
- }
618
- catch (error) {
619
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] Protocol V1 Initialize probe failed:', error);
620
- return false;
621
- }
622
- });
623
- }
624
- probeProtocolV2(path) {
625
- return __awaiter(this, void 0, void 0, function* () {
626
- if (!this.messages || !this.messagesV2) {
627
- return false;
628
- }
629
- return transport.probeProtocolV2({
630
- call: (name, data, options) => this.callProtocolV2(path, name, data, options),
631
- timeoutMs: PROTOCOL_PROBE_TIMEOUT,
632
- logger: this.Log,
633
- logPrefix: 'ProtocolV2 NodeUSB',
634
- onProbeFailed: () => this.resetConnectionAfterProbe(path),
635
- });
636
- });
637
- }
638
- writeProtocolV2Frame(path, frame) {
639
- var _a;
640
- return __awaiter(this, void 0, void 0, function* () {
641
- let lastError;
642
- for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt++) {
643
- if (this.cancelled) {
644
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
645
- }
646
- try {
647
- yield transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(frame));
648
- return;
649
- }
650
- catch (error) {
651
- lastError = error;
652
- const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryableError(error);
653
- if (!shouldRetry) {
654
- throw error;
655
- }
656
- try {
657
- yield this.reconnectForRetry(path, 'out', attempt, error);
658
- }
659
- catch (reconnectError) {
660
- lastError = reconnectError;
661
- (_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)}`);
662
- break;
663
- }
664
- }
665
- }
666
- throw lastError;
667
- });
668
- }
669
- receiveProtocolV2Frame(path, timeoutMs) {
670
- return __awaiter(this, void 0, void 0, function* () {
671
- let assembler = this.protocolV2Assemblers.get(path);
672
- if (!assembler) {
673
- assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES);
674
- this.protocolV2Assemblers.set(path, assembler);
675
- }
676
- let frame = assembler.push(new Uint8Array(0));
677
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
678
- while (!frame) {
679
- const transferIn = this.transferInWithRetry(path, this.getOpenDevice(path), transport.PROTOCOL_V2_FRAME_MAX_BYTES);
680
- const packet = deadline
681
- ? yield this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V2')
682
- : yield transferIn;
683
- const bytes = new Uint8Array(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
684
- try {
685
- frame = assembler.push(bytes);
686
- }
687
- catch (error) {
688
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
689
- }
690
- }
691
- return frame;
692
- });
693
- }
694
- callProtocolV2(path, name, data, options) {
695
- var _a;
696
- return __awaiter(this, void 0, void 0, function* () {
697
- const protocolV1Messages = this.messages;
698
- if (!this.messagesV2) {
699
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured, 'Protocol V2 schema not configured');
700
- }
701
- if (!protocolV1Messages) {
702
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
703
- }
704
- const session = new transport.ProtocolV2Session({
705
- schemas: {
706
- protocolV1: protocolV1Messages,
707
- protocolV2: this.messagesV2,
708
- },
709
- router: transport.PROTOCOL_V2_CHANNEL_USB,
710
- writeFrame: (frame) => this.writeProtocolV2Frame(path, frame),
711
- readFrame: () => this.receiveProtocolV2Frame(path, options === null || options === void 0 ? void 0 : options.timeoutMs),
712
- logger: this.Log,
713
- logPrefix: 'ProtocolV2 NodeUSB',
714
- createTimeoutError: (_messageName, timeoutMs) => new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`),
715
- });
716
- (_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
717
- return session.call(name, data, options);
718
- });
719
- }
720
- receiveData(path, dev, timeoutMs) {
721
- return __awaiter(this, void 0, void 0, function* () {
722
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
723
- const readPacket = () => __awaiter(this, void 0, void 0, function* () {
724
- const transferIn = this.transferInWithRetry(path, this.getOpenDevice(path), PACKET_SIZE);
725
- return deadline
726
- ? this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V1')
727
- : transferIn;
728
- });
729
- const firstPacket = timeoutMs
730
- ? yield readPacket()
731
- : yield this.transferInWithRetry(path, dev, PACKET_SIZE);
484
+ const firstPacket = yield this.transferInWithRetry(path, dev, PACKET_SIZE);
732
485
  const firstData = skipReportByte(firstPacket);
733
- const { length, typeId, restBuffer } = ProtocolV1.decodeFirstChunk(toArrayBuffer(firstData));
486
+ const { length, typeId, restBuffer } = decodeProtocol.decodeChunked(toArrayBuffer(firstData));
734
487
  const lengthWithHeader = Number(length) + HEADER_LENGTH;
735
488
  const decoded = new ByteBuffer__default["default"](lengthWithHeader);
736
489
  decoded.writeUint16(typeId);
@@ -739,7 +492,7 @@ class NodeUsbTransport {
739
492
  decoded.append(restBuffer);
740
493
  }
741
494
  while (decoded.offset < lengthWithHeader) {
742
- const packet = yield readPacket();
495
+ const packet = yield this.transferInWithRetry(path, this.getOpenDevice(path), PACKET_SIZE);
743
496
  const pktData = skipReportByte(packet);
744
497
  const buf = toArrayBuffer(pktData);
745
498
  if (lengthWithHeader - decoded.offset >= PAYLOAD_SIZE) {
@@ -754,10 +507,7 @@ class NodeUsbTransport {
754
507
  return Buffer.from(result).toString('hex');
755
508
  });
756
509
  }
757
- getProtocolType(path) {
758
- var _a;
759
- return (_a = this.deviceProtocol.get(path)) !== null && _a !== void 0 ? _a : 'V1';
760
- }
761
510
  }
762
511
 
763
- 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.34",
3
+ "version": "1.1.27-alpha.4",
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.34",
24
- "@onekeyfe/hd-transport": "1.1.27-alpha.34",
23
+ "@onekeyfe/hd-shared": "1.1.27-alpha.4",
24
+ "@onekeyfe/hd-transport": "1.1.27-alpha.4",
25
25
  "bytebuffer": "^5.0.1",
26
26
  "usb": "^2.14.0"
27
27
  },
28
- "gitHead": "ff1863a1ff300ada1314b7315e5ab827663d9f4c"
28
+ "gitHead": "93d0e7734f8c3a6380be10f74de203f0ba785bb8"
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,51 +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) ?? 'V1';
336
+ const { messages } = this;
385
337
  if (LogBlockCommand.has(name)) {
386
- this.Log?.debug('NodeUsbTransport call-', ' name: ', name, ' protocol: ', protocol);
338
+ this.Log?.debug('NodeUsbTransport call-', ' name: ', name);
387
339
  } else {
388
- this.Log?.debug(
389
- 'NodeUsbTransport call-',
390
- ' name: ',
391
- name,
392
- ' data: ',
393
- data,
394
- ' protocol: ',
395
- protocol
396
- );
397
- }
398
-
399
- if (protocol === 'V2') {
400
- return this.callProtocolV2(path, name, data, options);
340
+ this.Log?.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data);
401
341
  }
402
342
 
403
- return this.callProtocolV1(path, name, data, options);
404
- }
405
-
406
- private async callProtocolV1(
407
- path: string,
408
- name: string,
409
- data: Record<string, unknown>,
410
- options?: TransportCallOptions
411
- ) {
412
- const { messages } = this;
413
- if (!messages) {
414
- throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
415
- }
416
343
  // Encode protobuf message into 63-byte chunks (same as WebUsbTransport)
417
- const encodeBuffers = ProtocolV1.encodeMessageChunks(messages, name, data);
344
+ const encodeBuffers = buildEncodeBuffers(messages, name, data);
418
345
 
419
346
  // Send all chunks with retry — if any chunk fails and reconnects,
420
347
  // restart the entire send sequence from chunk 0 (device resets state on reconnect)
421
348
  await this.sendAllChunksWithRetry(path, encodeBuffers);
422
349
 
423
350
  // Receive response — re-resolve in case reconnect happened during send
424
- const resData = await this.receiveData(path, this.getOpenDevice(path), options?.timeoutMs);
351
+ const resData = await this.receiveData(path, this.getOpenDevice(path));
425
352
  if (typeof resData !== 'string') {
426
353
  throw ERRORS.TypedError(HardwareErrorCode.NetworkError, 'Returning data is not string.');
427
354
  }
428
- const jsonData = ProtocolV1.decodeMessage(messages, resData);
355
+ const jsonData = receiveOne(messages, resData);
429
356
  return check.call(jsonData);
430
357
  }
431
358
 
@@ -474,21 +401,6 @@ export default class NodeUsbTransport {
474
401
  );
475
402
  }
476
403
 
477
- private getDeviceInterface(dev: usb.Device): usb.Interface {
478
- const { interfaces } = dev;
479
- if (!interfaces?.length) {
480
- throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, 'USB interface not found');
481
- }
482
-
483
- const vendorInterface = interfaces.find(iface => iface.descriptor.bInterfaceClass === 0xff);
484
- const defaultInterface = interfaces.find(
485
- iface => iface.descriptor.bInterfaceNumber === INTERFACE_NUMBER
486
- );
487
- const iface = vendorInterface ?? defaultInterface ?? interfaces[0];
488
-
489
- return iface;
490
- }
491
-
492
404
  /**
493
405
  * Reconnect device before retrying a failed transfer (aligned with WebUsbTransport).
494
406
  * Uses per-path lock to prevent concurrent reconnects to the same device.
@@ -511,9 +423,9 @@ export default class NodeUsbTransport {
511
423
  );
512
424
  await wait(attempt * PACKET_IO_RETRY_DELAY);
513
425
 
514
- // Close the existing device without clearing the detected protocol cache.
426
+ // Close the existing device
515
427
  try {
516
- await this.closeOpenDevice(path);
428
+ await this.release(path);
517
429
  } catch (releaseError) {
518
430
  this.Log?.debug('[NodeUsbTransport] release before retry error:', releaseError);
519
431
  }
@@ -640,7 +552,7 @@ export default class NodeUsbTransport {
640
552
  try {
641
553
  dev.timeout = TRANSFER_TIMEOUT_MS;
642
554
 
643
- const iface = this.getDeviceInterface(dev);
555
+ const iface = dev.interface(INTERFACE_NUMBER);
644
556
 
645
557
  // On Linux, detach kernel driver if active
646
558
  if (process.platform === 'linux') {
@@ -655,14 +567,12 @@ export default class NodeUsbTransport {
655
567
 
656
568
  iface.claim();
657
569
 
658
- const epIn =
659
- iface.endpoints.find(
660
- (e): e is usb.InEndpoint => e.direction === 'in' && e.address === ENDPOINT_IN
661
- ) ?? iface.endpoints.find((e): e is usb.InEndpoint => e.direction === 'in');
662
- const epOut =
663
- iface.endpoints.find(
664
- (e): e is usb.OutEndpoint => e.direction === 'out' && e.address === ENDPOINT_OUT
665
- ) ?? 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
+ );
666
576
 
667
577
  if (!epIn || !epOut) {
668
578
  throw ERRORS.TypedError(
@@ -685,259 +595,17 @@ export default class NodeUsbTransport {
685
595
  }
686
596
  }
687
597
 
688
- private createProtocolMismatchError(expected: ProtocolType) {
689
- return ERRORS.TypedError(
690
- HardwareErrorCode.RuntimeError,
691
- `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`
692
- );
693
- }
694
-
695
- private async detectProtocol(
696
- path: string,
697
- expectedProtocol?: ProtocolType
698
- ): Promise<ProtocolType> {
699
- if (expectedProtocol === 'V1') {
700
- if (await this.probeProtocolV1(path)) {
701
- this.deviceProtocol.set(path, 'V1');
702
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
703
- return 'V1';
704
- }
705
- throw this.createProtocolMismatchError(expectedProtocol);
706
- }
707
-
708
- if (expectedProtocol === 'V2') {
709
- if (await this.probeProtocolV2(path)) {
710
- this.deviceProtocol.set(path, 'V2');
711
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
712
- return 'V2';
713
- }
714
- throw this.createProtocolMismatchError(expectedProtocol);
715
- }
716
-
717
- if (this.deviceProtocol.get(path) === 'V2' && (await this.probeProtocolV2(path))) {
718
- this.deviceProtocol.set(path, 'V2');
719
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
720
- return 'V2';
721
- }
722
-
723
- let protocol: ProtocolType = 'V1';
724
- if (!(await this.probeProtocolV1(path)) && (await this.probeProtocolV2(path))) {
725
- protocol = 'V2';
726
- }
727
- this.deviceProtocol.set(path, protocol);
728
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> ${protocol}`);
729
- return protocol;
730
- }
731
-
732
- private async resetConnectionAfterProbe(path: string) {
733
- this.protocolV2Assemblers.get(path)?.reset();
734
-
735
- try {
736
- await this.closeOpenDevice(path);
737
- } catch (error) {
738
- this.Log?.debug('[NodeUsbTransport] close after protocol probe error:', error);
739
- }
740
-
741
- await this.enumerate();
742
- this.openDevice(path);
743
- }
744
-
745
- private async withProtocolReadTimeout<T>(
746
- path: string,
747
- promise: Promise<T>,
748
- timeoutMs: number,
749
- protocol: ProtocolType
750
- ): Promise<T> {
751
- let timer: ReturnType<typeof setTimeout> | undefined;
752
- let timedOut = false;
753
- const waitForeverAfterTimeout = () => new Promise<never>(() => {});
754
- const guardedPromise = promise.then(
755
- value => (timedOut ? waitForeverAfterTimeout() : value),
756
- error => {
757
- if (timedOut) {
758
- return waitForeverAfterTimeout();
759
- }
760
- throw error;
761
- }
762
- );
763
- try {
764
- return await Promise.race([
765
- guardedPromise,
766
- new Promise<never>((_, reject) => {
767
- timer = setTimeout(async () => {
768
- timedOut = true;
769
- try {
770
- await this.resetConnectionAfterProbe(path);
771
- } catch (error) {
772
- this.Log?.debug(
773
- `[NodeUsbTransport] reset after Protocol ${protocol} timeout failed:`,
774
- error
775
- );
776
- } finally {
777
- reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
778
- }
779
- }, timeoutMs);
780
- }),
781
- ]);
782
- } finally {
783
- if (timer) clearTimeout(timer);
784
- }
785
- }
786
-
787
- private async probeProtocolV1(path: string) {
788
- if (!this.messages) {
789
- return false;
790
- }
791
-
792
- try {
793
- await this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
794
- return true;
795
- } catch (error) {
796
- this.Log?.debug('[NodeUsbTransport] Protocol V1 Initialize probe failed:', error);
797
- return false;
798
- }
799
- }
800
-
801
- private async probeProtocolV2(path: string) {
802
- if (!this.messages || !this.messagesV2) {
803
- return false;
804
- }
805
-
806
- return probeProtocolV2Helper({
807
- call: (name, data, options) => this.callProtocolV2(path, name, data, options),
808
- timeoutMs: PROTOCOL_PROBE_TIMEOUT,
809
- logger: this.Log,
810
- logPrefix: 'ProtocolV2 NodeUSB',
811
- onProbeFailed: () => this.resetConnectionAfterProbe(path),
812
- });
813
- }
814
-
815
- private async writeProtocolV2Frame(path: string, frame: Uint8Array) {
816
- let lastError: unknown;
817
- for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt++) {
818
- if (this.cancelled) {
819
- throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
820
- }
821
- try {
822
- await transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(frame));
823
- return;
824
- } catch (error) {
825
- lastError = error;
826
- const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryableError(error);
827
- if (!shouldRetry) {
828
- throw error;
829
- }
830
- try {
831
- await this.reconnectForRetry(path, 'out', attempt, error);
832
- } catch (reconnectError) {
833
- lastError = reconnectError;
834
- this.Log?.debug(
835
- `[NodeUsbTransport] Protocol V2 write reconnect failed on retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(
836
- reconnectError
837
- )}`
838
- );
839
- break;
840
- }
841
- }
842
- }
843
- throw lastError;
844
- }
845
-
846
- private async receiveProtocolV2Frame(path: string, timeoutMs?: number): Promise<Uint8Array> {
847
- let assembler = this.protocolV2Assemblers.get(path);
848
- if (!assembler) {
849
- assembler = new ProtocolV2FrameAssembler(PROTOCOL_V2_FRAME_MAX_BYTES);
850
- this.protocolV2Assemblers.set(path, assembler);
851
- }
852
-
853
- let frame: Uint8Array | undefined = assembler.push(new Uint8Array(0));
854
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
855
-
856
- while (!frame) {
857
- const transferIn = this.transferInWithRetry(
858
- path,
859
- this.getOpenDevice(path),
860
- PROTOCOL_V2_FRAME_MAX_BYTES
861
- );
862
- const packet = deadline
863
- ? await this.withProtocolReadTimeout(
864
- path,
865
- transferIn,
866
- Math.max(deadline - Date.now(), 1),
867
- 'V2'
868
- )
869
- : await transferIn;
870
- const bytes = new Uint8Array(
871
- packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength)
872
- );
873
- try {
874
- frame = assembler.push(bytes);
875
- } catch (error) {
876
- throw ERRORS.TypedError(
877
- HardwareErrorCode.NetworkError,
878
- error instanceof Error ? error.message : String(error)
879
- );
880
- }
881
- }
882
- return frame;
883
- }
884
-
885
- private async callProtocolV2(
886
- path: string,
887
- name: string,
888
- data: Record<string, unknown>,
889
- options?: TransportCallOptions
890
- ) {
891
- const protocolV1Messages = this.messages;
892
- if (!this.messagesV2) {
893
- throw ERRORS.TypedError(
894
- HardwareErrorCode.TransportNotConfigured,
895
- 'Protocol V2 schema not configured'
896
- );
897
- }
898
- if (!protocolV1Messages) {
899
- throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
900
- }
901
-
902
- const session = new ProtocolV2Session({
903
- schemas: {
904
- protocolV1: protocolV1Messages,
905
- protocolV2: this.messagesV2,
906
- },
907
- router: PROTOCOL_V2_CHANNEL_USB,
908
- writeFrame: (frame: Uint8Array) => this.writeProtocolV2Frame(path, frame),
909
- readFrame: () => this.receiveProtocolV2Frame(path, options?.timeoutMs),
910
- logger: this.Log,
911
- logPrefix: 'ProtocolV2 NodeUSB',
912
- createTimeoutError: (_messageName: string, timeoutMs: number) =>
913
- new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`),
914
- });
915
-
916
- this.protocolV2Assemblers.get(path)?.reset();
917
- return session.call(name, data, options);
918
- }
919
-
920
598
  /**
921
599
  * Receive a complete protobuf response from the device.
922
600
  * Reads 64-byte packets, strips 0x3F marker, reassembles into hex string.
923
601
  */
924
- private async receiveData(path: string, dev: OpenDevice, timeoutMs?: number): Promise<string> {
925
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
926
- const readPacket = async () => {
927
- const transferIn = this.transferInWithRetry(path, this.getOpenDevice(path), PACKET_SIZE);
928
- return deadline
929
- ? this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V1')
930
- : transferIn;
931
- };
932
-
602
+ private async receiveData(path: string, dev: OpenDevice): Promise<string> {
933
603
  // Read first packet, skip report byte
934
- const firstPacket = timeoutMs
935
- ? await readPacket()
936
- : await this.transferInWithRetry(path, dev, PACKET_SIZE);
604
+ const firstPacket = await this.transferInWithRetry(path, dev, PACKET_SIZE);
937
605
  const firstData = skipReportByte(firstPacket);
938
606
 
939
607
  // Decode header: ## marker → { typeId, length, restBuffer }
940
- const { length, typeId, restBuffer } = ProtocolV1.decodeFirstChunk(toArrayBuffer(firstData));
608
+ const { length, typeId, restBuffer } = decodeProtocol.decodeChunked(toArrayBuffer(firstData));
941
609
 
942
610
  // Allocate result: typeId(2) + length(4) + payload(length)
943
611
  const lengthWithHeader = Number(length) + HEADER_LENGTH;
@@ -951,7 +619,7 @@ export default class NodeUsbTransport {
951
619
  // Read subsequent packets until complete
952
620
  // Re-resolve device on each iteration so we use a fresh handle after any reconnect
953
621
  while (decoded.offset < lengthWithHeader) {
954
- const packet = await readPacket();
622
+ const packet = await this.transferInWithRetry(path, this.getOpenDevice(path), PACKET_SIZE);
955
623
  const pktData = skipReportByte(packet);
956
624
  const buf = toArrayBuffer(pktData);
957
625
  if (lengthWithHeader - decoded.offset >= PAYLOAD_SIZE) {
@@ -965,8 +633,6 @@ export default class NodeUsbTransport {
965
633
  const result = decoded.toBuffer();
966
634
  return Buffer.from(result as unknown as ArrayBuffer).toString('hex');
967
635
  }
968
-
969
- getProtocolType(path: string): ProtocolType {
970
- return this.deviceProtocol.get(path) ?? 'V1';
971
- }
972
636
  }
637
+
638
+ export { PACKET_SIZE } from './constants';