@onekeyfe/hd-transport-web-device 1.1.27-alpha.31 → 1.1.27-alpha.32

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.
@@ -23,12 +23,17 @@ export default class ElectronAutoBleTransport {
23
23
  emitter?: EventEmitter;
24
24
  private connectedDevices;
25
25
  private deviceProtocol;
26
+ private deviceProtocolHints;
26
27
  private v1Buffers;
27
28
  private v2Assemblers;
28
- private v2FrameQueue;
29
- private v2FramePromise;
29
+ private v2FrameQueues;
30
+ private v2FramePromises;
31
+ private activeProtocolV2Call;
32
+ private nextProtocolV2CallToken;
30
33
  private notificationCleanups;
31
34
  private disconnectCleanups;
35
+ private notificationTokens;
36
+ private nextNotificationToken;
32
37
  private handleBluetoothError;
33
38
  private cleanupDeviceState;
34
39
  init(logger: any, emitter?: EventEmitter): void;
@@ -50,15 +55,19 @@ export default class ElectronAutoBleTransport {
50
55
  release(id: string): Promise<void>;
51
56
  private createProtocolMismatchError;
52
57
  private detectProtocol;
58
+ private createNotificationSubscription;
59
+ private resetProbeStateAfterProtocolProbe;
53
60
  private probeProtocolV1;
54
61
  private probeProtocolV2;
55
62
  private writeWithChunking;
56
63
  private writeWithRetry;
57
64
  private handleNotification;
58
65
  private handleProtocolV2Notification;
66
+ private getProtocolV2FrameQueue;
59
67
  private resolveProtocolV2Frame;
60
- private rejectProtocolV2Frame;
68
+ private rejectAllProtocolV2Frames;
61
69
  private resetProtocolV2Frames;
70
+ private isActiveProtocolV2Call;
62
71
  private readProtocolV2Frame;
63
72
  private handleProtocolV1Notification;
64
73
  call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<import("@onekeyfe/hd-transport").MessageFromOneKey>;
@@ -1 +1 @@
1
- {"version":3,"file":"electron-auto-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-auto-ble-transport.ts"],"names":[],"mappings":";AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAWvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AAyCF,MAAM,CAAC,OAAO,OAAO,wBAAwB;IAC3C,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA8B;IAElC,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,YAAY,CAAoB;IAExC,OAAO,CAAC,cAAc,CAAqC;IAE3D,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAK7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IAqF9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAexB,OAAO,CAAC,2BAA2B;YAOrB,cAAc;YAqCd,eAAe;YAef,eAAe;YAmBf,iBAAiB;YAsBjB,cAAc;IA4B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,4BAA4B;IA6BpC,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,qBAAqB;YAKf,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAyBlB,cAAc;YAuEd,cAAc;IA2E5B,OAAO,CAAC,6BAA6B;IAgDrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
1
+ {"version":3,"file":"electron-auto-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-auto-ble-transport.ts"],"names":[],"mappings":";AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAWvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AAsCF,MAAM,CAAC,OAAO,OAAO,wBAAwB;IAC3C,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA8B;IAElC,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,oBAAoB,CAAgD;IAE5E,OAAO,CAAC,uBAAuB,CAAK;IAEpC,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IAyB1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAK7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IAuF9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAexB,OAAO,CAAC,2BAA2B;YAOrB,cAAc;IAgD5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAoCjC,eAAe;YAef,eAAe;YAmBf,iBAAiB;YAsBjB,cAAc;IA4B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,4BAA4B;IA6BpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAIhB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAyBlB,cAAc;YAuEd,cAAc;IAmF5B,OAAO,CAAC,6BAA6B;IAgDrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ declare class WebUsbTransport {
13
13
  messages: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
14
14
  messagesV2: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
15
15
  private deviceProtocol;
16
+ private deviceProtocolHints;
16
17
  private protocolV2Assemblers;
17
18
  private deviceEndpoints;
18
19
  name: string;
@@ -118,12 +119,17 @@ declare class ElectronAutoBleTransport {
118
119
  emitter?: EventEmitter;
119
120
  private connectedDevices;
120
121
  private deviceProtocol;
122
+ private deviceProtocolHints;
121
123
  private v1Buffers;
122
124
  private v2Assemblers;
123
- private v2FrameQueue;
124
- private v2FramePromise;
125
+ private v2FrameQueues;
126
+ private v2FramePromises;
127
+ private activeProtocolV2Call;
128
+ private nextProtocolV2CallToken;
125
129
  private notificationCleanups;
126
130
  private disconnectCleanups;
131
+ private notificationTokens;
132
+ private nextNotificationToken;
127
133
  private handleBluetoothError;
128
134
  private cleanupDeviceState;
129
135
  init(logger: any, emitter?: EventEmitter): void;
@@ -145,15 +151,19 @@ declare class ElectronAutoBleTransport {
145
151
  release(id: string): Promise<void>;
146
152
  private createProtocolMismatchError;
147
153
  private detectProtocol;
154
+ private createNotificationSubscription;
155
+ private resetProbeStateAfterProtocolProbe;
148
156
  private probeProtocolV1;
149
157
  private probeProtocolV2;
150
158
  private writeWithChunking;
151
159
  private writeWithRetry;
152
160
  private handleNotification;
153
161
  private handleProtocolV2Notification;
162
+ private getProtocolV2FrameQueue;
154
163
  private resolveProtocolV2Frame;
155
- private rejectProtocolV2Frame;
164
+ private rejectAllProtocolV2Frames;
156
165
  private resetProtocolV2Frames;
166
+ private isActiveProtocolV2Call;
157
167
  private readProtocolV2Frame;
158
168
  private handleProtocolV1Notification;
159
169
  call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<_onekeyfe_hd_transport.MessageFromOneKey>;
package/dist/index.js CHANGED
@@ -61,9 +61,13 @@ function shouldBlockWebUsbCallDataLog(name) {
61
61
  const normalized = name.replace(/[_\s-]/g, '');
62
62
  return transport.LogBlockCommand.has(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
63
63
  }
64
+ function inferProtocolHintFromDeviceName$1(name) {
65
+ return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
66
+ }
64
67
  class WebUsbTransport {
65
68
  constructor() {
66
69
  this.deviceProtocol = new Map();
70
+ this.deviceProtocolHints = new Map();
67
71
  this.protocolV2Assemblers = new Map();
68
72
  this.deviceEndpoints = new Map();
69
73
  this.name = 'WebUsbTransport';
@@ -122,11 +126,18 @@ class WebUsbTransport {
122
126
  const hasSerialNumber = typeof dev.serialNumber === 'string' && dev.serialNumber.length > 0;
123
127
  return isOneKey && hasSerialNumber;
124
128
  });
125
- this.deviceList = onekeyDevices.map(device => ({
126
- path: device.serialNumber,
127
- device,
128
- commType: 'webusb',
129
- }));
129
+ this.deviceList = onekeyDevices.map(device => {
130
+ const path = device.serialNumber;
131
+ const protocolHint = inferProtocolHintFromDeviceName$1(device.productName);
132
+ if (protocolHint) {
133
+ this.deviceProtocolHints.set(path, protocolHint);
134
+ }
135
+ return {
136
+ path,
137
+ device,
138
+ commType: 'webusb',
139
+ };
140
+ });
130
141
  for (const dev of onekeyDevices) {
131
142
  this.Log.debug(`[WebUSB] Device: name="${dev.productName}" serial="${dev.serialNumber}" ` +
132
143
  `VID=0x${dev.vendorId.toString(16)} PID=0x${dev.productId.toString(16)}`);
@@ -135,13 +146,20 @@ class WebUsbTransport {
135
146
  });
136
147
  }
137
148
  acquire(input) {
138
- var _a;
149
+ var _a, _b, _c;
139
150
  return __awaiter(this, void 0, void 0, function* () {
140
151
  if (!input.path)
141
152
  return;
142
153
  try {
143
154
  yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
144
- yield this.detectProtocol(input.path, input.expectedProtocol);
155
+ const deviceName = (_b = this.deviceList.find(device => device.path === input.path)) === null || _b === void 0 ? void 0 : _b.device.productName;
156
+ const protocolHint = input.expectedProtocol
157
+ ? undefined
158
+ : (_c = this.deviceProtocolHints.get(input.path)) !== null && _c !== void 0 ? _c : inferProtocolHintFromDeviceName$1(deviceName);
159
+ if (protocolHint) {
160
+ this.deviceProtocolHints.set(input.path, protocolHint);
161
+ }
162
+ yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
145
163
  return yield Promise.resolve(input.path);
146
164
  }
147
165
  catch (e) {
@@ -153,7 +171,7 @@ class WebUsbTransport {
153
171
  createProtocolMismatchError(expected) {
154
172
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
155
173
  }
156
- detectProtocol(path, expectedProtocol) {
174
+ detectProtocol(path, expectedProtocol, protocolHint) {
157
175
  return __awaiter(this, void 0, void 0, function* () {
158
176
  if (expectedProtocol === 'V1') {
159
177
  if (yield this.probeProtocolV1(path)) {
@@ -171,6 +189,11 @@ class WebUsbTransport {
171
189
  }
172
190
  throw this.createProtocolMismatchError(expectedProtocol);
173
191
  }
192
+ if (protocolHint === 'V2' && (yield this.probeProtocolV2(path))) {
193
+ this.deviceProtocol.set(path, 'V2');
194
+ this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (hint)`);
195
+ return 'V2';
196
+ }
174
197
  if (this.deviceProtocol.get(path) === 'V2' && (yield this.probeProtocolV2(path))) {
175
198
  this.deviceProtocol.set(path, 'V2');
176
199
  this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
@@ -641,6 +664,7 @@ class WebUsbTransport {
641
664
  yield device.releaseInterface(ifaceNum);
642
665
  yield device.close();
643
666
  this.deviceProtocol.delete(path);
667
+ this.deviceProtocolHints.delete(path);
644
668
  (_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
645
669
  this.protocolV2Assemblers.delete(path);
646
670
  this.deviceEndpoints.delete(path);
@@ -951,13 +975,10 @@ function shouldSuppressHighVolumeCallLog(name) {
951
975
  return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
952
976
  }
953
977
  const { parseConfigure, ProtocolV1, check } = transport__default["default"];
954
- function inferProtocolTypeFromDeviceName(name) {
978
+ function inferProtocolHintFromDeviceName(name) {
955
979
  return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
956
980
  }
957
- const toBleDescriptor = (device, protocolType) => {
958
- const resolvedProtocolType = protocolType !== null && protocolType !== void 0 ? protocolType : inferProtocolTypeFromDeviceName(device.name);
959
- return Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (resolvedProtocolType ? { protocolType: resolvedProtocolType } : {}));
960
- };
981
+ const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
961
982
  const BLE_PACKET_SIZE = 192;
962
983
  const BLE_WRITE_DELAY_MS = 5;
963
984
  const BLE_WRITE_MAX_RETRIES = 3;
@@ -971,12 +992,17 @@ class ElectronAutoBleTransport {
971
992
  this.runPromise = null;
972
993
  this.connectedDevices = new Set();
973
994
  this.deviceProtocol = new Map();
995
+ this.deviceProtocolHints = new Map();
974
996
  this.v1Buffers = new Map();
975
997
  this.v2Assemblers = new Map();
976
- this.v2FrameQueue = [];
977
- this.v2FramePromise = null;
998
+ this.v2FrameQueues = new Map();
999
+ this.v2FramePromises = new Map();
1000
+ this.activeProtocolV2Call = null;
1001
+ this.nextProtocolV2CallToken = 1;
978
1002
  this.notificationCleanups = new Map();
979
1003
  this.disconnectCleanups = new Map();
1004
+ this.notificationTokens = new Map();
1005
+ this.nextNotificationToken = 1;
980
1006
  }
981
1007
  handleBluetoothError(error) {
982
1008
  if (error && typeof error === 'object') {
@@ -1008,10 +1034,17 @@ class ElectronAutoBleTransport {
1008
1034
  throw error;
1009
1035
  }
1010
1036
  cleanupDeviceState(deviceId) {
1037
+ var _a;
1011
1038
  this.connectedDevices.delete(deviceId);
1012
1039
  this.deviceProtocol.delete(deviceId);
1040
+ this.deviceProtocolHints.delete(deviceId);
1013
1041
  this.v1Buffers.delete(deviceId);
1014
1042
  this.v2Assemblers.delete(deviceId);
1043
+ this.resetProtocolV2Frames(deviceId);
1044
+ if (((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === deviceId) {
1045
+ this.activeProtocolV2Call = null;
1046
+ }
1047
+ this.notificationTokens.delete(deviceId);
1015
1048
  const notifyCleanup = this.notificationCleanups.get(deviceId);
1016
1049
  if (notifyCleanup) {
1017
1050
  notifyCleanup();
@@ -1057,6 +1090,10 @@ class ElectronAutoBleTransport {
1057
1090
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Auto BLE] enumerate found ${devices.length} device(s):`);
1058
1091
  for (const dev of devices) {
1059
1092
  (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Auto BLE] id="${dev.id}" name="${dev.name}"`);
1093
+ const protocolHint = inferProtocolHintFromDeviceName(dev.name);
1094
+ if (protocolHint) {
1095
+ this.deviceProtocolHints.set(dev.id, protocolHint);
1096
+ }
1060
1097
  }
1061
1098
  return devices.map(device => toBleDescriptor(device));
1062
1099
  }
@@ -1067,7 +1104,7 @@ class ElectronAutoBleTransport {
1067
1104
  });
1068
1105
  }
1069
1106
  acquire(input) {
1070
- var _a, _b, _c, _d, _e;
1107
+ var _a, _b, _c, _d, _e, _f;
1071
1108
  return __awaiter(this, void 0, void 0, function* () {
1072
1109
  const { uuid, forceCleanRunPromise, expectedProtocol } = input;
1073
1110
  if (!uuid) {
@@ -1076,7 +1113,9 @@ class ElectronAutoBleTransport {
1076
1113
  if (forceCleanRunPromise && this.runPromise) {
1077
1114
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1078
1115
  this.runPromise.reject(error);
1079
- this.rejectProtocolV2Frame(error);
1116
+ this.rejectAllProtocolV2Frames(error);
1117
+ this.runPromise = null;
1118
+ this.activeProtocolV2Call = null;
1080
1119
  }
1081
1120
  try {
1082
1121
  if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
@@ -1086,6 +1125,12 @@ class ElectronAutoBleTransport {
1086
1125
  if (!device) {
1087
1126
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${uuid} not found`);
1088
1127
  }
1128
+ const protocolHint = expectedProtocol
1129
+ ? undefined
1130
+ : (_b = this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(device.name);
1131
+ if (protocolHint) {
1132
+ this.deviceProtocolHints.set(uuid, protocolHint);
1133
+ }
1089
1134
  try {
1090
1135
  yield window.desktopApi.nobleBle.connect(uuid);
1091
1136
  this.connectedDevices.add(uuid);
@@ -1096,11 +1141,7 @@ class ElectronAutoBleTransport {
1096
1141
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
1097
1142
  this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
1098
1143
  yield window.desktopApi.nobleBle.subscribe(uuid);
1099
- const cleanup = window.desktopApi.nobleBle.onNotification((deviceId, data) => {
1100
- if (deviceId === uuid) {
1101
- this.handleNotification(uuid, data);
1102
- }
1103
- });
1144
+ const cleanup = this.createNotificationSubscription(uuid);
1104
1145
  this.notificationCleanups.set(uuid, cleanup);
1105
1146
  const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
1106
1147
  var _a;
@@ -1114,8 +1155,8 @@ class ElectronAutoBleTransport {
1114
1155
  }
1115
1156
  });
1116
1157
  this.disconnectCleanups.set(uuid, disconnectCleanup);
1117
- const protocolType = yield this.detectProtocol(uuid, expectedProtocol);
1118
- (_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
1158
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
1159
+ (_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit('device-connect', {
1119
1160
  name: device.name,
1120
1161
  id: device.id,
1121
1162
  connectId: device.id,
@@ -1123,15 +1164,15 @@ class ElectronAutoBleTransport {
1123
1164
  return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
1124
1165
  }
1125
1166
  catch (error) {
1126
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Auto BLE] acquire failed:', error);
1167
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Auto BLE] acquire failed:', error);
1127
1168
  try {
1128
- if (((_d = window.desktopApi) === null || _d === void 0 ? void 0 : _d.nobleBle) && this.connectedDevices.has(uuid)) {
1169
+ if (((_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) && this.connectedDevices.has(uuid)) {
1129
1170
  yield window.desktopApi.nobleBle.unsubscribe(uuid);
1130
1171
  yield window.desktopApi.nobleBle.disconnect(uuid);
1131
1172
  }
1132
1173
  }
1133
1174
  catch (cleanupError) {
1134
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug('[Auto BLE] acquire cleanup failed:', cleanupError);
1175
+ (_f = this.Log) === null || _f === void 0 ? void 0 : _f.debug('[Auto BLE] acquire cleanup failed:', cleanupError);
1135
1176
  }
1136
1177
  this.cleanupDeviceState(uuid);
1137
1178
  throw error;
@@ -1159,8 +1200,8 @@ class ElectronAutoBleTransport {
1159
1200
  createProtocolMismatchError(expected) {
1160
1201
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1161
1202
  }
1162
- detectProtocol(uuid, expectedProtocol) {
1163
- var _a, _b, _c, _d;
1203
+ detectProtocol(uuid, expectedProtocol, protocolHint) {
1204
+ var _a, _b, _c, _d, _e;
1164
1205
  return __awaiter(this, void 0, void 0, function* () {
1165
1206
  if (expectedProtocol === 'V1') {
1166
1207
  if (yield this.probeProtocolV1(uuid)) {
@@ -1178,20 +1219,80 @@ class ElectronAutoBleTransport {
1178
1219
  }
1179
1220
  throw this.createProtocolMismatchError(expectedProtocol);
1180
1221
  }
1222
+ if (protocolHint === 'V2' && (yield this.probeProtocolV2(uuid))) {
1223
+ this.deviceProtocol.set(uuid, 'V2');
1224
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (hint)`);
1225
+ return 'V2';
1226
+ }
1181
1227
  if (this.deviceProtocol.get(uuid) === 'V2' && (yield this.probeProtocolV2(uuid))) {
1182
1228
  this.deviceProtocol.set(uuid, 'V2');
1183
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (cached)`);
1229
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (cached)`);
1184
1230
  return 'V2';
1185
1231
  }
1186
1232
  let protocol = 'V1';
1187
- if (!(yield this.probeProtocolV1(uuid)) && (yield this.probeProtocolV2(uuid))) {
1188
- protocol = 'V2';
1233
+ const protocolV1Detected = yield this.probeProtocolV1(uuid);
1234
+ if (!protocolV1Detected) {
1235
+ yield this.resetProbeStateAfterProtocolProbe(uuid, 'V1');
1236
+ if (yield this.probeProtocolV2(uuid)) {
1237
+ protocol = 'V2';
1238
+ }
1189
1239
  }
1190
1240
  this.deviceProtocol.set(uuid, protocol);
1191
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
1241
+ (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
1192
1242
  return protocol;
1193
1243
  });
1194
1244
  }
1245
+ createNotificationSubscription(uuid) {
1246
+ var _a;
1247
+ if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
1248
+ throw new Error('Noble BLE API not available');
1249
+ }
1250
+ const notificationToken = this.nextNotificationToken;
1251
+ this.nextNotificationToken += 1;
1252
+ this.notificationTokens.set(uuid, notificationToken);
1253
+ return window.desktopApi.nobleBle.onNotification((deviceId, data) => {
1254
+ if (deviceId === uuid && this.notificationTokens.get(uuid) === notificationToken) {
1255
+ this.handleNotification(uuid, data);
1256
+ }
1257
+ });
1258
+ }
1259
+ resetProbeStateAfterProtocolProbe(uuid, protocol) {
1260
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1261
+ return __awaiter(this, void 0, void 0, function* () {
1262
+ this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
1263
+ (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1264
+ this.resetProtocolV2Frames(uuid);
1265
+ if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1266
+ this.activeProtocolV2Call = null;
1267
+ }
1268
+ if (this.runPromise) {
1269
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1270
+ this.runPromise.reject(error);
1271
+ this.runPromise = null;
1272
+ }
1273
+ const notifyCleanup = this.notificationCleanups.get(uuid);
1274
+ if (notifyCleanup) {
1275
+ notifyCleanup();
1276
+ this.notificationCleanups.delete(uuid);
1277
+ }
1278
+ this.notificationTokens.delete(uuid);
1279
+ try {
1280
+ yield ((_d = (_c = window.desktopApi) === null || _c === void 0 ? void 0 : _c.nobleBle) === null || _d === void 0 ? void 0 : _d.unsubscribe(uuid));
1281
+ }
1282
+ catch (error) {
1283
+ (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[Auto BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
1284
+ }
1285
+ try {
1286
+ yield ((_g = (_f = window.desktopApi) === null || _f === void 0 ? void 0 : _f.nobleBle) === null || _g === void 0 ? void 0 : _g.subscribe(uuid));
1287
+ }
1288
+ catch (error) {
1289
+ (_h = this.Log) === null || _h === void 0 ? void 0 : _h.debug(`[Auto BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
1290
+ throw error;
1291
+ }
1292
+ const cleanup = this.createNotificationSubscription(uuid);
1293
+ this.notificationCleanups.set(uuid, cleanup);
1294
+ });
1295
+ }
1195
1296
  probeProtocolV1(uuid) {
1196
1297
  var _a;
1197
1298
  return __awaiter(this, void 0, void 0, function* () {
@@ -1225,7 +1326,7 @@ class ElectronAutoBleTransport {
1225
1326
  onProbeFailed: () => {
1226
1327
  var _a;
1227
1328
  (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1228
- this.resetProtocolV2Frames();
1329
+ this.resetProtocolV2Frames(uuid);
1229
1330
  },
1230
1331
  });
1231
1332
  });
@@ -1280,7 +1381,7 @@ class ElectronAutoBleTransport {
1280
1381
  if (this.runPromise) {
1281
1382
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
1282
1383
  this.runPromise.reject(error);
1283
- this.rejectProtocolV2Frame(error);
1384
+ this.rejectAllProtocolV2Frames(error);
1284
1385
  }
1285
1386
  return;
1286
1387
  }
@@ -1292,11 +1393,11 @@ class ElectronAutoBleTransport {
1292
1393
  this.handleProtocolV1Notification(deviceId, hexData);
1293
1394
  }
1294
1395
  handleProtocolV2Notification(deviceId, hexData) {
1295
- var _a, _b;
1396
+ var _a, _b, _c;
1296
1397
  try {
1297
- if (!this.runPromise) {
1298
- (_a = this.v2Assemblers.get(deviceId)) === null || _a === void 0 ? void 0 : _a.reset();
1299
- this.resetProtocolV2Frames();
1398
+ if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== deviceId) {
1399
+ (_b = this.v2Assemblers.get(deviceId)) === null || _b === void 0 ? void 0 : _b.reset();
1400
+ this.resetProtocolV2Frames(deviceId);
1300
1401
  return;
1301
1402
  }
1302
1403
  const bytes = transport.hexToBytes(hexData);
@@ -1307,52 +1408,65 @@ class ElectronAutoBleTransport {
1307
1408
  return;
1308
1409
  let frameData = assembler.push(bytes);
1309
1410
  while (frameData) {
1310
- this.resolveProtocolV2Frame(frameData);
1411
+ this.resolveProtocolV2Frame(deviceId, frameData);
1311
1412
  frameData = assembler.push(new Uint8Array(0));
1312
1413
  }
1313
1414
  }
1314
1415
  catch (error) {
1315
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Auto BLE] Protocol V2 notification error:', error);
1416
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Auto BLE] Protocol V2 notification error:', error);
1316
1417
  if (this.runPromise) {
1317
1418
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1318
1419
  this.runPromise.reject(notifyError);
1319
- this.rejectProtocolV2Frame(notifyError);
1420
+ this.rejectAllProtocolV2Frames(notifyError);
1320
1421
  }
1321
1422
  }
1322
1423
  }
1323
- resolveProtocolV2Frame(frame) {
1324
- if (this.v2FramePromise) {
1325
- this.v2FramePromise.resolve(frame);
1326
- this.v2FramePromise = null;
1424
+ getProtocolV2FrameQueue(uuid) {
1425
+ let queue = this.v2FrameQueues.get(uuid);
1426
+ if (!queue) {
1427
+ queue = [];
1428
+ this.v2FrameQueues.set(uuid, queue);
1429
+ }
1430
+ return queue;
1431
+ }
1432
+ resolveProtocolV2Frame(uuid, frame) {
1433
+ const framePromise = this.v2FramePromises.get(uuid);
1434
+ if (framePromise) {
1435
+ framePromise.resolve(frame);
1436
+ this.v2FramePromises.delete(uuid);
1327
1437
  return;
1328
1438
  }
1329
- this.v2FrameQueue.push(frame);
1439
+ this.getProtocolV2FrameQueue(uuid).push(frame);
1330
1440
  }
1331
- rejectProtocolV2Frame(error) {
1332
- this.v2FrameQueue = [];
1333
- if (this.v2FramePromise) {
1334
- this.v2FramePromise.reject(error);
1335
- this.v2FramePromise = null;
1441
+ rejectAllProtocolV2Frames(error) {
1442
+ this.v2FrameQueues.clear();
1443
+ for (const framePromise of this.v2FramePromises.values()) {
1444
+ framePromise.reject(error);
1336
1445
  }
1446
+ this.v2FramePromises.clear();
1337
1447
  }
1338
- resetProtocolV2Frames() {
1339
- this.v2FrameQueue = [];
1340
- this.v2FramePromise = null;
1448
+ resetProtocolV2Frames(uuid) {
1449
+ this.v2FrameQueues.delete(uuid);
1450
+ this.v2FramePromises.delete(uuid);
1341
1451
  }
1342
- readProtocolV2Frame() {
1452
+ isActiveProtocolV2Call(uuid, token) {
1453
+ var _a;
1454
+ return ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === uuid && this.activeProtocolV2Call.token === token;
1455
+ }
1456
+ readProtocolV2Frame(uuid) {
1343
1457
  return __awaiter(this, void 0, void 0, function* () {
1344
- const queuedFrame = this.v2FrameQueue.shift();
1458
+ const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
1345
1459
  if (queuedFrame) {
1346
1460
  return queuedFrame;
1347
1461
  }
1348
1462
  const framePromise = hdShared.createDeferred();
1349
- this.v2FramePromise = framePromise;
1463
+ this.v2FramePromises.set(uuid, framePromise);
1350
1464
  try {
1351
1465
  return yield framePromise.promise;
1352
1466
  }
1353
1467
  finally {
1354
- if (this.v2FramePromise === framePromise) {
1355
- this.v2FramePromise = null;
1468
+ if (this.v2FramePromises.get(uuid) === framePromise) {
1469
+ this.v2FramePromises.delete(uuid);
1356
1470
  }
1357
1471
  }
1358
1472
  });
@@ -1469,14 +1583,17 @@ class ElectronAutoBleTransport {
1469
1583
  }
1470
1584
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1471
1585
  this.runPromise.reject(error);
1472
- this.rejectProtocolV2Frame(error);
1586
+ this.rejectAllProtocolV2Frames(error);
1473
1587
  this.runPromise = null;
1588
+ this.activeProtocolV2Call = null;
1474
1589
  }
1475
1590
  const runPromise = hdShared.createDeferred();
1476
1591
  runPromise.promise.catch(() => undefined);
1477
1592
  this.runPromise = runPromise;
1593
+ const callToken = this.nextProtocolV2CallToken++;
1594
+ this.activeProtocolV2Call = { uuid, token: callToken };
1478
1595
  (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1479
- this.resetProtocolV2Frames();
1596
+ this.resetProtocolV2Frames(uuid);
1480
1597
  let completed = false;
1481
1598
  const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : BLE_RESPONSE_TIMEOUT_MS });
1482
1599
  try {
@@ -1488,7 +1605,7 @@ class ElectronAutoBleTransport {
1488
1605
  router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1489
1606
  writeFrame: (frame) => this.writeWithChunking(uuid, transport.bytesToHex(frame)),
1490
1607
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1491
- const rxFrame = yield this.readProtocolV2Frame();
1608
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1492
1609
  if (!(rxFrame instanceof Uint8Array)) {
1493
1610
  throw new Error('Response is not Uint8Array');
1494
1611
  }
@@ -1503,16 +1620,21 @@ class ElectronAutoBleTransport {
1503
1620
  return result;
1504
1621
  }
1505
1622
  catch (e) {
1506
- (_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1507
- this.resetProtocolV2Frames();
1623
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
1624
+ (_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1625
+ this.resetProtocolV2Frames(uuid);
1626
+ }
1508
1627
  (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Auto BLE] Protocol V2 call error:', e);
1509
1628
  throw e;
1510
1629
  }
1511
1630
  finally {
1512
- if (!completed) {
1513
- (_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
1631
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
1632
+ if (!completed) {
1633
+ (_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
1634
+ }
1635
+ this.resetProtocolV2Frames(uuid);
1636
+ this.activeProtocolV2Call = null;
1514
1637
  }
1515
- this.resetProtocolV2Frames();
1516
1638
  if (this.runPromise === runPromise) {
1517
1639
  this.runPromise = null;
1518
1640
  }
package/dist/webusb.d.ts CHANGED
@@ -10,6 +10,7 @@ export default class WebUsbTransport {
10
10
  messages: ReturnType<typeof transport.parseConfigure> | undefined;
11
11
  messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
12
12
  private deviceProtocol;
13
+ private deviceProtocolHints;
13
14
  private protocolV2Assemblers;
14
15
  private deviceEndpoints;
15
16
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AA6BhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,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;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAG9D,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,eAAe,CAA2C;IAElE,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAU7B,kBAAkB;IAmBlB,SAAS;IAQT,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IAiBjC,OAAO,CAAC,2BAA2B;YAOrB,cAAc;IAwCtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAgC5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YAoCpB,mBAAmB;YA2CnB,yBAAyB;YAuBzB,uBAAuB;YA4CvB,eAAe;YAcf,eAAe;IAiBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA4BlB,cAAc;YAkCd,cAAc;YAmCd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAgB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
1
+ {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAiChC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,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;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,eAAe,CAA2C;IAElE,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAU7B,kBAAkB;IAmBlB,SAAS;IAQT,mBAAmB;IAwCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IAyBjC,OAAO,CAAC,2BAA2B;YAOrB,cAAc;IA+CtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAgC5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YAoCpB,mBAAmB;YA2CnB,yBAAyB;YAuBzB,uBAAuB;YA4CvB,eAAe;YAcf,eAAe;IAiBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA4BlB,cAAc;YAkCd,cAAc;YAmCd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAiB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.1.27-alpha.31",
3
+ "version": "1.1.27-alpha.32",
4
4
  "author": "OneKey",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -20,13 +20,13 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-shared": "1.1.27-alpha.31",
24
- "@onekeyfe/hd-transport": "1.1.27-alpha.31"
23
+ "@onekeyfe/hd-shared": "1.1.27-alpha.32",
24
+ "@onekeyfe/hd-transport": "1.1.27-alpha.32"
25
25
  },
26
26
  "devDependencies": {
27
- "@onekeyfe/hd-transport-electron": "1.1.27-alpha.31",
27
+ "@onekeyfe/hd-transport-electron": "1.1.27-alpha.32",
28
28
  "@types/w3c-web-usb": "^1.0.6",
29
29
  "@types/web-bluetooth": "^0.0.17"
30
30
  },
31
- "gitHead": "73a8ecbc0e5e47038966b2f9a5ab30dc2f947d8f"
31
+ "gitHead": "04f92052a18dcf17e4a347f3003b4c4bbf062681"
32
32
  }
@@ -49,25 +49,22 @@ interface PacketProcessResult {
49
49
  error?: string;
50
50
  }
51
51
 
52
- function inferProtocolTypeFromDeviceName(name?: string | null): ProtocolType | undefined {
52
+ function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
53
53
  return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
54
54
  }
55
55
 
56
56
  const toBleDescriptor = (
57
57
  device: { id: string; name: string | null },
58
58
  protocolType?: ProtocolType
59
- ): OneKeyDeviceInfo => {
60
- const resolvedProtocolType = protocolType ?? inferProtocolTypeFromDeviceName(device.name);
61
-
62
- return {
59
+ ): OneKeyDeviceInfo =>
60
+ ({
63
61
  id: device.id,
64
62
  name: device.name,
65
63
  path: device.id,
66
64
  debug: false,
67
65
  commType: 'electron-ble',
68
- ...(resolvedProtocolType ? { protocolType: resolvedProtocolType } : {}),
69
- } as OneKeyDeviceInfo;
70
- };
66
+ ...(protocolType ? { protocolType } : {}),
67
+ } as OneKeyDeviceInfo);
71
68
 
72
69
  const BLE_PACKET_SIZE = 192;
73
70
  const BLE_WRITE_DELAY_MS = 5;
@@ -101,18 +98,28 @@ export default class ElectronAutoBleTransport {
101
98
 
102
99
  private deviceProtocol: Map<string, ProtocolType> = new Map();
103
100
 
101
+ private deviceProtocolHints: Map<string, ProtocolType> = new Map();
102
+
104
103
  private v1Buffers: Map<string, { buffer: number[]; bufferLength: number }> = new Map();
105
104
 
106
105
  private v2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
107
106
 
108
- private v2FrameQueue: Uint8Array[] = [];
107
+ private v2FrameQueues: Map<string, Uint8Array[]> = new Map();
108
+
109
+ private v2FramePromises: Map<string, Deferred<Uint8Array>> = new Map();
109
110
 
110
- private v2FramePromise: Deferred<Uint8Array> | null = null;
111
+ private activeProtocolV2Call: { uuid: string; token: number } | null = null;
112
+
113
+ private nextProtocolV2CallToken = 1;
111
114
 
112
115
  private notificationCleanups: Map<string, () => void> = new Map();
113
116
 
114
117
  private disconnectCleanups: Map<string, () => void> = new Map();
115
118
 
119
+ private notificationTokens: Map<string, number> = new Map();
120
+
121
+ private nextNotificationToken = 1;
122
+
116
123
  private handleBluetoothError(error: any): never {
117
124
  if (error && typeof error === 'object') {
118
125
  if ('code' in error) {
@@ -147,8 +154,14 @@ export default class ElectronAutoBleTransport {
147
154
  private cleanupDeviceState(deviceId: string): void {
148
155
  this.connectedDevices.delete(deviceId);
149
156
  this.deviceProtocol.delete(deviceId);
157
+ this.deviceProtocolHints.delete(deviceId);
150
158
  this.v1Buffers.delete(deviceId);
151
159
  this.v2Assemblers.delete(deviceId);
160
+ this.resetProtocolV2Frames(deviceId);
161
+ if (this.activeProtocolV2Call?.uuid === deviceId) {
162
+ this.activeProtocolV2Call = null;
163
+ }
164
+ this.notificationTokens.delete(deviceId);
152
165
 
153
166
  const notifyCleanup = this.notificationCleanups.get(deviceId);
154
167
  if (notifyCleanup) {
@@ -200,6 +213,10 @@ export default class ElectronAutoBleTransport {
200
213
  this.Log?.debug(`[Auto BLE] enumerate found ${devices.length} device(s):`);
201
214
  for (const dev of devices) {
202
215
  this.Log?.debug(`[Auto BLE] id="${dev.id}" name="${dev.name}"`);
216
+ const protocolHint = inferProtocolHintFromDeviceName(dev.name);
217
+ if (protocolHint) {
218
+ this.deviceProtocolHints.set(dev.id, protocolHint);
219
+ }
203
220
  }
204
221
  return devices.map(device => toBleDescriptor(device));
205
222
  } catch (error) {
@@ -218,7 +235,9 @@ export default class ElectronAutoBleTransport {
218
235
  if (forceCleanRunPromise && this.runPromise) {
219
236
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
220
237
  this.runPromise.reject(error);
221
- this.rejectProtocolV2Frame(error);
238
+ this.rejectAllProtocolV2Frames(error);
239
+ this.runPromise = null;
240
+ this.activeProtocolV2Call = null;
222
241
  }
223
242
 
224
243
  try {
@@ -230,6 +249,12 @@ export default class ElectronAutoBleTransport {
230
249
  if (!device) {
231
250
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, `Device ${uuid} not found`);
232
251
  }
252
+ const protocolHint = expectedProtocol
253
+ ? undefined
254
+ : this.deviceProtocolHints.get(uuid) ?? inferProtocolHintFromDeviceName(device.name);
255
+ if (protocolHint) {
256
+ this.deviceProtocolHints.set(uuid, protocolHint);
257
+ }
233
258
 
234
259
  try {
235
260
  await window.desktopApi.nobleBle.connect(uuid);
@@ -243,13 +268,7 @@ export default class ElectronAutoBleTransport {
243
268
 
244
269
  await window.desktopApi.nobleBle.subscribe(uuid);
245
270
 
246
- const cleanup = window.desktopApi.nobleBle.onNotification(
247
- (deviceId: string, data: string) => {
248
- if (deviceId === uuid) {
249
- this.handleNotification(uuid, data);
250
- }
251
- }
252
- );
271
+ const cleanup = this.createNotificationSubscription(uuid);
253
272
  this.notificationCleanups.set(uuid, cleanup);
254
273
 
255
274
  const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected(
@@ -266,7 +285,7 @@ export default class ElectronAutoBleTransport {
266
285
  );
267
286
  this.disconnectCleanups.set(uuid, disconnectCleanup);
268
287
 
269
- const protocolType = await this.detectProtocol(uuid, expectedProtocol);
288
+ const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
270
289
 
271
290
  this.emitter?.emit('device-connect', {
272
291
  name: device.name,
@@ -317,7 +336,8 @@ export default class ElectronAutoBleTransport {
317
336
 
318
337
  private async detectProtocol(
319
338
  uuid: string,
320
- expectedProtocol?: ProtocolType
339
+ expectedProtocol?: ProtocolType,
340
+ protocolHint?: ProtocolType
321
341
  ): Promise<ProtocolType> {
322
342
  if (expectedProtocol === 'V1') {
323
343
  if (await this.probeProtocolV1(uuid)) {
@@ -337,6 +357,12 @@ export default class ElectronAutoBleTransport {
337
357
  throw this.createProtocolMismatchError(expectedProtocol);
338
358
  }
339
359
 
360
+ if (protocolHint === 'V2' && (await this.probeProtocolV2(uuid))) {
361
+ this.deviceProtocol.set(uuid, 'V2');
362
+ this.Log?.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (hint)`);
363
+ return 'V2';
364
+ }
365
+
340
366
  if (this.deviceProtocol.get(uuid) === 'V2' && (await this.probeProtocolV2(uuid))) {
341
367
  this.deviceProtocol.set(uuid, 'V2');
342
368
  this.Log?.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (cached)`);
@@ -344,14 +370,70 @@ export default class ElectronAutoBleTransport {
344
370
  }
345
371
 
346
372
  let protocol: ProtocolType = 'V1';
347
- if (!(await this.probeProtocolV1(uuid)) && (await this.probeProtocolV2(uuid))) {
348
- protocol = 'V2';
373
+ const protocolV1Detected = await this.probeProtocolV1(uuid);
374
+ if (!protocolV1Detected) {
375
+ await this.resetProbeStateAfterProtocolProbe(uuid, 'V1');
376
+ if (await this.probeProtocolV2(uuid)) {
377
+ protocol = 'V2';
378
+ }
349
379
  }
350
380
  this.deviceProtocol.set(uuid, protocol);
351
381
  this.Log?.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
352
382
  return protocol;
353
383
  }
354
384
 
385
+ private createNotificationSubscription(uuid: string) {
386
+ if (!window.desktopApi?.nobleBle) {
387
+ throw new Error('Noble BLE API not available');
388
+ }
389
+
390
+ const notificationToken = this.nextNotificationToken;
391
+ this.nextNotificationToken += 1;
392
+ this.notificationTokens.set(uuid, notificationToken);
393
+
394
+ return window.desktopApi.nobleBle.onNotification((deviceId: string, data: string) => {
395
+ if (deviceId === uuid && this.notificationTokens.get(uuid) === notificationToken) {
396
+ this.handleNotification(uuid, data);
397
+ }
398
+ });
399
+ }
400
+
401
+ private async resetProbeStateAfterProtocolProbe(uuid: string, protocol: ProtocolType) {
402
+ this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
403
+ this.v2Assemblers.get(uuid)?.reset();
404
+ this.resetProtocolV2Frames(uuid);
405
+ if (this.activeProtocolV2Call?.uuid === uuid) {
406
+ this.activeProtocolV2Call = null;
407
+ }
408
+ if (this.runPromise) {
409
+ const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
410
+ this.runPromise.reject(error);
411
+ this.runPromise = null;
412
+ }
413
+
414
+ const notifyCleanup = this.notificationCleanups.get(uuid);
415
+ if (notifyCleanup) {
416
+ notifyCleanup();
417
+ this.notificationCleanups.delete(uuid);
418
+ }
419
+ this.notificationTokens.delete(uuid);
420
+
421
+ try {
422
+ await window.desktopApi?.nobleBle?.unsubscribe(uuid);
423
+ } catch (error) {
424
+ this.Log?.debug(`[Auto BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
425
+ }
426
+ try {
427
+ await window.desktopApi?.nobleBle?.subscribe(uuid);
428
+ } catch (error) {
429
+ this.Log?.debug(`[Auto BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
430
+ throw error;
431
+ }
432
+
433
+ const cleanup = this.createNotificationSubscription(uuid);
434
+ this.notificationCleanups.set(uuid, cleanup);
435
+ }
436
+
355
437
  private async probeProtocolV1(uuid: string) {
356
438
  if (!this._messages) {
357
439
  return false;
@@ -381,7 +463,7 @@ export default class ElectronAutoBleTransport {
381
463
  logPrefix: 'ProtocolV2 BLE',
382
464
  onProbeFailed: () => {
383
465
  this.v2Assemblers.get(uuid)?.reset();
384
- this.resetProtocolV2Frames();
466
+ this.resetProtocolV2Frames(uuid);
385
467
  },
386
468
  });
387
469
  }
@@ -442,7 +524,7 @@ export default class ElectronAutoBleTransport {
442
524
  if (this.runPromise) {
443
525
  const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
444
526
  this.runPromise.reject(error);
445
- this.rejectProtocolV2Frame(error);
527
+ this.rejectAllProtocolV2Frames(error);
446
528
  }
447
529
  return;
448
530
  }
@@ -457,9 +539,9 @@ export default class ElectronAutoBleTransport {
457
539
 
458
540
  private handleProtocolV2Notification(deviceId: string, hexData: string): void {
459
541
  try {
460
- if (!this.runPromise) {
542
+ if (!this.runPromise || this.activeProtocolV2Call?.uuid !== deviceId) {
461
543
  this.v2Assemblers.get(deviceId)?.reset();
462
- this.resetProtocolV2Frames();
544
+ this.resetProtocolV2Frames(deviceId);
463
545
  return;
464
546
  }
465
547
 
@@ -471,7 +553,7 @@ export default class ElectronAutoBleTransport {
471
553
 
472
554
  let frameData = assembler.push(bytes);
473
555
  while (frameData) {
474
- this.resolveProtocolV2Frame(frameData);
556
+ this.resolveProtocolV2Frame(deviceId, frameData);
475
557
  frameData = assembler.push(new Uint8Array(0));
476
558
  }
477
559
  } catch (error) {
@@ -479,46 +561,60 @@ export default class ElectronAutoBleTransport {
479
561
  if (this.runPromise) {
480
562
  const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
481
563
  this.runPromise.reject(notifyError);
482
- this.rejectProtocolV2Frame(notifyError);
564
+ this.rejectAllProtocolV2Frames(notifyError);
483
565
  }
484
566
  }
485
567
  }
486
568
 
487
- private resolveProtocolV2Frame(frame: Uint8Array) {
488
- if (this.v2FramePromise) {
489
- this.v2FramePromise.resolve(frame);
490
- this.v2FramePromise = null;
569
+ private getProtocolV2FrameQueue(uuid: string) {
570
+ let queue = this.v2FrameQueues.get(uuid);
571
+ if (!queue) {
572
+ queue = [];
573
+ this.v2FrameQueues.set(uuid, queue);
574
+ }
575
+ return queue;
576
+ }
577
+
578
+ private resolveProtocolV2Frame(uuid: string, frame: Uint8Array) {
579
+ const framePromise = this.v2FramePromises.get(uuid);
580
+ if (framePromise) {
581
+ framePromise.resolve(frame);
582
+ this.v2FramePromises.delete(uuid);
491
583
  return;
492
584
  }
493
- this.v2FrameQueue.push(frame);
585
+ this.getProtocolV2FrameQueue(uuid).push(frame);
494
586
  }
495
587
 
496
- private rejectProtocolV2Frame(error: Error) {
497
- this.v2FrameQueue = [];
498
- if (this.v2FramePromise) {
499
- this.v2FramePromise.reject(error);
500
- this.v2FramePromise = null;
588
+ private rejectAllProtocolV2Frames(error: Error) {
589
+ this.v2FrameQueues.clear();
590
+ for (const framePromise of this.v2FramePromises.values()) {
591
+ framePromise.reject(error);
501
592
  }
593
+ this.v2FramePromises.clear();
594
+ }
595
+
596
+ private resetProtocolV2Frames(uuid: string) {
597
+ this.v2FrameQueues.delete(uuid);
598
+ this.v2FramePromises.delete(uuid);
502
599
  }
503
600
 
504
- private resetProtocolV2Frames() {
505
- this.v2FrameQueue = [];
506
- this.v2FramePromise = null;
601
+ private isActiveProtocolV2Call(uuid: string, token: number) {
602
+ return this.activeProtocolV2Call?.uuid === uuid && this.activeProtocolV2Call.token === token;
507
603
  }
508
604
 
509
- private async readProtocolV2Frame() {
510
- const queuedFrame = this.v2FrameQueue.shift();
605
+ private async readProtocolV2Frame(uuid: string) {
606
+ const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
511
607
  if (queuedFrame) {
512
608
  return queuedFrame;
513
609
  }
514
610
 
515
611
  const framePromise = createDeferred<Uint8Array>();
516
- this.v2FramePromise = framePromise;
612
+ this.v2FramePromises.set(uuid, framePromise);
517
613
  try {
518
614
  return await framePromise.promise;
519
615
  } finally {
520
- if (this.v2FramePromise === framePromise) {
521
- this.v2FramePromise = null;
616
+ if (this.v2FramePromises.get(uuid) === framePromise) {
617
+ this.v2FramePromises.delete(uuid);
522
618
  }
523
619
  }
524
620
  }
@@ -656,15 +752,18 @@ export default class ElectronAutoBleTransport {
656
752
  }
657
753
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
658
754
  this.runPromise.reject(error);
659
- this.rejectProtocolV2Frame(error);
755
+ this.rejectAllProtocolV2Frames(error);
660
756
  this.runPromise = null;
757
+ this.activeProtocolV2Call = null;
661
758
  }
662
759
 
663
760
  const runPromise = createDeferred<Uint8Array | string>();
664
761
  runPromise.promise.catch(() => undefined);
665
762
  this.runPromise = runPromise;
763
+ const callToken = this.nextProtocolV2CallToken++;
764
+ this.activeProtocolV2Call = { uuid, token: callToken };
666
765
  this.v2Assemblers.get(uuid)?.reset();
667
- this.resetProtocolV2Frames();
766
+ this.resetProtocolV2Frames(uuid);
668
767
  let completed = false;
669
768
  const callOptions = {
670
769
  ...options,
@@ -680,7 +779,7 @@ export default class ElectronAutoBleTransport {
680
779
  router: PROTOCOL_V2_CHANNEL_BLE_UART,
681
780
  writeFrame: (frame: Uint8Array) => this.writeWithChunking(uuid, bytesToHex(frame)),
682
781
  readFrame: async () => {
683
- const rxFrame = await this.readProtocolV2Frame();
782
+ const rxFrame = await this.readProtocolV2Frame(uuid);
684
783
  if (!(rxFrame instanceof Uint8Array)) {
685
784
  throw new Error('Response is not Uint8Array');
686
785
  }
@@ -699,15 +798,20 @@ export default class ElectronAutoBleTransport {
699
798
  completed = true;
700
799
  return result;
701
800
  } catch (e) {
702
- this.v2Assemblers.get(uuid)?.reset();
703
- this.resetProtocolV2Frames();
801
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
802
+ this.v2Assemblers.get(uuid)?.reset();
803
+ this.resetProtocolV2Frames(uuid);
804
+ }
704
805
  this.Log?.error('[Auto BLE] Protocol V2 call error:', e);
705
806
  throw e;
706
807
  } finally {
707
- if (!completed) {
708
- this.v2Assemblers.get(uuid)?.reset();
808
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
809
+ if (!completed) {
810
+ this.v2Assemblers.get(uuid)?.reset();
811
+ }
812
+ this.resetProtocolV2Frames(uuid);
813
+ this.activeProtocolV2Call = null;
709
814
  }
710
- this.resetProtocolV2Frames();
711
815
  if (this.runPromise === runPromise) {
712
816
  this.runPromise = null;
713
817
  }
package/src/webusb.ts CHANGED
@@ -45,6 +45,10 @@ function shouldBlockWebUsbCallDataLog(name: string) {
45
45
  return LogBlockCommand.has(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
46
46
  }
47
47
 
48
+ function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
49
+ return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
50
+ }
51
+
48
52
  /**
49
53
  * Device information with path and WebUSB device instance
50
54
  */
@@ -74,6 +78,8 @@ export default class WebUsbTransport {
74
78
  /** Per-path protocol type detected by active wire-level probe. */
75
79
  private deviceProtocol: Map<string, ProtocolType> = new Map();
76
80
 
81
+ private deviceProtocolHints: Map<string, ProtocolType> = new Map();
82
+
77
83
  /** 按设备缓存 Protocol V2 frame assembler,保留同一次读取里多出来的后续 frame。 */
78
84
  private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
79
85
 
@@ -179,11 +185,19 @@ export default class WebUsbTransport {
179
185
  return isOneKey && hasSerialNumber;
180
186
  });
181
187
 
182
- this.deviceList = onekeyDevices.map(device => ({
183
- path: device.serialNumber as string,
184
- device,
185
- commType: 'webusb',
186
- }));
188
+ this.deviceList = onekeyDevices.map(device => {
189
+ const path = device.serialNumber as string;
190
+ const protocolHint = inferProtocolHintFromDeviceName(device.productName);
191
+ if (protocolHint) {
192
+ this.deviceProtocolHints.set(path, protocolHint);
193
+ }
194
+
195
+ return {
196
+ path,
197
+ device,
198
+ commType: 'webusb',
199
+ };
200
+ });
187
201
 
188
202
  // Debug: log all discovered devices. Protocol is detected after acquire via wire probe.
189
203
  for (const dev of onekeyDevices) {
@@ -203,7 +217,15 @@ export default class WebUsbTransport {
203
217
  if (!input.path) return;
204
218
  try {
205
219
  await this.connect(input.path ?? '', true);
206
- await this.detectProtocol(input.path, input.expectedProtocol);
220
+ const deviceName = this.deviceList.find(device => device.path === input.path)?.device
221
+ .productName;
222
+ const protocolHint = input.expectedProtocol
223
+ ? undefined
224
+ : this.deviceProtocolHints.get(input.path) ?? inferProtocolHintFromDeviceName(deviceName);
225
+ if (protocolHint) {
226
+ this.deviceProtocolHints.set(input.path, protocolHint);
227
+ }
228
+ await this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
207
229
  return await Promise.resolve(input.path);
208
230
  } catch (e) {
209
231
  this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
@@ -225,7 +247,8 @@ export default class WebUsbTransport {
225
247
 
226
248
  private async detectProtocol(
227
249
  path: string,
228
- expectedProtocol?: ProtocolType
250
+ expectedProtocol?: ProtocolType,
251
+ protocolHint?: ProtocolType
229
252
  ): Promise<ProtocolType> {
230
253
  if (expectedProtocol === 'V1') {
231
254
  if (await this.probeProtocolV1(path)) {
@@ -245,6 +268,12 @@ export default class WebUsbTransport {
245
268
  throw this.createProtocolMismatchError(expectedProtocol);
246
269
  }
247
270
 
271
+ if (protocolHint === 'V2' && (await this.probeProtocolV2(path))) {
272
+ this.deviceProtocol.set(path, 'V2');
273
+ this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (hint)`);
274
+ return 'V2';
275
+ }
276
+
248
277
  if (this.deviceProtocol.get(path) === 'V2' && (await this.probeProtocolV2(path))) {
249
278
  this.deviceProtocol.set(path, 'V2');
250
279
  this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
@@ -689,8 +718,8 @@ export default class WebUsbTransport {
689
718
  /**
690
719
  * Send/receive a single call over Protocol V2 (0x5A framing).
691
720
  *
692
- * Encoding: protobuf message → 2-byte LE msgType + pb bytes → Protocol V2 frame
693
- * Decoding: Protocol V2 frame → msgType + pb bytes → protobuf message
721
+ * Encoding: protobuf message → 2-byte LE messageTypeId + pb bytes → Protocol V2 frame
722
+ * Decoding: Protocol V2 frame → messageTypeId + pb bytes → protobuf message
694
723
  */
695
724
  private async callProtocolV2(
696
725
  path: string,
@@ -826,6 +855,7 @@ export default class WebUsbTransport {
826
855
  await device.releaseInterface(ifaceNum);
827
856
  await device.close();
828
857
  this.deviceProtocol.delete(path);
858
+ this.deviceProtocolHints.delete(path);
829
859
  this.protocolV2Assemblers.get(path)?.reset();
830
860
  this.protocolV2Assemblers.delete(path);
831
861
  this.deviceEndpoints.delete(path);