made-bridge 0.3.2 → 0.3.3
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.
- package/package.json +1 -1
- package/src/board-link.mjs +3 -0
- package/src/server.mjs +4 -2
- package/src/usb-receiver.mjs +25 -3
package/package.json
CHANGED
package/src/board-link.mjs
CHANGED
|
@@ -248,6 +248,9 @@ export function createBoardLink() {
|
|
|
248
248
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) throw fail('事件无效');
|
|
249
249
|
if (typeof payload.name !== 'string' || !CAPABILITY_NAME.test(payload.name)) throw fail('事件无效');
|
|
250
250
|
const fields = parseFields(payload.fields);
|
|
251
|
+
// 设备事件一律落日志(含未订阅被拒的):调试/烧机观察通道依赖它,
|
|
252
|
+
// 例如 burn-in 压测的水位事件 burnin.mem 就没有任何插件订阅。
|
|
253
|
+
console.log('[board-event]', deviceId.slice(-6), payload.name, JSON.stringify(fields));
|
|
251
254
|
const catalog = catalogs.get(deviceId);
|
|
252
255
|
if (!catalog || !catalog.events.some((item) => item.name === payload.name)) throw fail('事件不在板侧清单');
|
|
253
256
|
const subscribed = this.subscription(deviceId, runtime, config);
|
package/src/server.mjs
CHANGED
|
@@ -582,7 +582,9 @@ export function createApp(config, { run, transcribe, tunnel: providedTunnel, ena
|
|
|
582
582
|
}
|
|
583
583
|
}
|
|
584
584
|
if (!queued) throw new Error(lastError || '命令未能下发');
|
|
585
|
-
|
|
585
|
+
// USB 分片上传 240×320 帧实测 >15s(base64 走请求通道),15s 会
|
|
586
|
+
// 误报失败——其实 PNG 随后正常落盘。放宽到 45s。
|
|
587
|
+
const deadline = Date.now() + 45000;
|
|
586
588
|
while (Date.now() < deadline) {
|
|
587
589
|
const file = screenshots.latest(before);
|
|
588
590
|
if (file) {
|
|
@@ -599,7 +601,7 @@ export function createApp(config, { run, transcribe, tunnel: providedTunnel, ena
|
|
|
599
601
|
}
|
|
600
602
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
601
603
|
}
|
|
602
|
-
throw new Error('码得未在
|
|
604
|
+
throw new Error('码得未在 45 秒内回传截图(固件需要更新到支持 debug.screenshot 的版本)');
|
|
603
605
|
}
|
|
604
606
|
if (url.pathname === '/api/firmware') {
|
|
605
607
|
const type = String(req.headers['content-type'] || '');
|
package/src/usb-receiver.mjs
CHANGED
|
@@ -220,6 +220,9 @@ export class UsbReceiverProtocol {
|
|
|
220
220
|
|
|
221
221
|
feed(chunk) {
|
|
222
222
|
if (this.closed) return;
|
|
223
|
+
if (chunk.includes(0x22) && chunk.includes(0x3a) && chunk.length > 60 && !chunk.slice(0, 30).includes('hello')) {
|
|
224
|
+
console.log('[usb-raw]', chunk.length, JSON.stringify(chunk.subarray(0, 120).toString('utf8')));
|
|
225
|
+
}
|
|
223
226
|
let start = 0;
|
|
224
227
|
for (let index = 0; index < chunk.length; index += 1) {
|
|
225
228
|
if (chunk[index] !== 10) continue;
|
|
@@ -264,7 +267,7 @@ export class UsbReceiverProtocol {
|
|
|
264
267
|
console.error(`[usb-link] log: ${line.slice(0, 240)}`);
|
|
265
268
|
return;
|
|
266
269
|
}
|
|
267
|
-
console.error(`[usb-link] unparseable
|
|
270
|
+
console.error(`[usb-link] unparseable len=${line.length} tail=${JSON.stringify(line.slice(-40))}`);
|
|
268
271
|
this.rejectCurrent(400, 'USB 帧格式无效');
|
|
269
272
|
return;
|
|
270
273
|
}
|
|
@@ -272,6 +275,9 @@ export class UsbReceiverProtocol {
|
|
|
272
275
|
this.rejectCurrent(400, 'USB 帧格式无效');
|
|
273
276
|
return;
|
|
274
277
|
}
|
|
278
|
+
if (frame && typeof frame.type === 'string' && frame.type !== 'hello') {
|
|
279
|
+
console.log('[usb-frame]', frame.type, frame.id ?? '', String(frame.path || '').slice(0, 60));
|
|
280
|
+
}
|
|
275
281
|
if (frame.type === 'hello') {
|
|
276
282
|
if (frame.version !== 1 || ![undefined, 'receiver', 'direct'].includes(frame.mode)) return;
|
|
277
283
|
const direct = frame.mode === 'direct';
|
|
@@ -285,13 +291,25 @@ export class UsbReceiverProtocol {
|
|
|
285
291
|
ssid: direct ? '' : frame.ssid, password: direct ? '' : frame.password };
|
|
286
292
|
if (this.peer && (this.peer.mode !== peer.mode || this.peer.deviceId !== peer.deviceId)) return;
|
|
287
293
|
if (this.onHello(peer) === false) return;
|
|
288
|
-
console.error(`[usb-link] hello: mode=${peer.mode} ${new Date().toISOString()}`);
|
|
294
|
+
console.error(`[usb-link] hello: mode=${peer.mode} diag=${frame.diag || '-'} ${new Date().toISOString()}`);
|
|
289
295
|
this.peer = peer;
|
|
290
296
|
this.ackWindow = peer.mode === 'direct' ? ACK_WINDOW_DIRECT : ACK_WINDOW;
|
|
291
297
|
this.helloReceived = true;
|
|
292
298
|
this.onActivity();
|
|
293
299
|
// The receiver waits for this acknowledgement before forwarding HTTP.
|
|
294
300
|
// A USB cable being present does not mean the desktop process is ready.
|
|
301
|
+
// ready 直发不进 pendingWork:pendingWork 同时承载 writeResponse 的
|
|
302
|
+
// ack 流控等待——设备偶发丢 ack 时该链最长堵 15s,期间所有重握手
|
|
303
|
+
// 都等不到 ready,设备侧表现为连环 E263 + UI 闪「连接电脑」(实锤
|
|
304
|
+
// 于 2026-10-20 冒烟:ack TIMEOUT id=52 前后 15s 全是 1.5s 间隔握手
|
|
305
|
+
// 超时)。sendLine 走 link.writeQueue,帧原子性与 pendingWork 相同;
|
|
306
|
+
// direct 固件 read_request_frame 本就支持在响应流中处理 ready。
|
|
307
|
+
// receiver 模式保持原排队路径(ready 语义 = 桥接进程就绪)。
|
|
308
|
+
if (peer.mode === 'direct' && !this.closed) {
|
|
309
|
+
this.sendLine(JSON.stringify({ type: 'ready', version: 1, connectionId: this.connectionId }))
|
|
310
|
+
.catch(() => this.dispose());
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
295
313
|
const generation = this.generation;
|
|
296
314
|
this.pendingWork = this.pendingWork.then(async () => {
|
|
297
315
|
if (!this.closed && this.helloReceived && this.generation === generation) {
|
|
@@ -318,6 +336,7 @@ export class UsbReceiverProtocol {
|
|
|
318
336
|
return;
|
|
319
337
|
}
|
|
320
338
|
if (frame.type === 'request') {
|
|
339
|
+
console.log('[usb-req]', frame.method, frame.path, 'id=' + frame.id);
|
|
321
340
|
if (this.current || this.responding || !validId(frame.id)) {
|
|
322
341
|
if (this.current) this.rejectCurrent(409, 'USB 请求重叠');
|
|
323
342
|
return;
|
|
@@ -382,6 +401,7 @@ export class UsbReceiverProtocol {
|
|
|
382
401
|
return;
|
|
383
402
|
}
|
|
384
403
|
if (frame.type === 'end') {
|
|
404
|
+
console.log('[usb-end] id=' + current.id + ' received=' + current.received + ' path=' + current.path.slice(0, 40));
|
|
385
405
|
if (current.received !== current.length) {
|
|
386
406
|
this.rejectCurrent(400, 'USB 请求长度不符');
|
|
387
407
|
return;
|
|
@@ -392,6 +412,8 @@ export class UsbReceiverProtocol {
|
|
|
392
412
|
try {
|
|
393
413
|
if (this.closed || this.current !== current || current.controller.signal.aborted) return;
|
|
394
414
|
const response = await this.forward(request, current.controller.signal);
|
|
415
|
+
console.log('[usb-resp]', response.status, request.method, request.path.slice(0, 40),
|
|
416
|
+
'body=' + (response.body ? response.body.length : -1));
|
|
395
417
|
if (this.closed || this.current !== current) return;
|
|
396
418
|
clearTimeout(current.timer);
|
|
397
419
|
await this.writeResponse(current.id, response.status, response.contentType, response.body);
|
|
@@ -485,7 +507,7 @@ export class UsbReceiverProtocol {
|
|
|
485
507
|
console.error(`[usb-link] ack TIMEOUT id=${id} ${new Date().toISOString()}`);
|
|
486
508
|
this.ackWaiters.delete(id);
|
|
487
509
|
reject(new Error('USB ACK 超时'));
|
|
488
|
-
},
|
|
510
|
+
}, 3_000);
|
|
489
511
|
timer.unref?.();
|
|
490
512
|
const check = () => {
|
|
491
513
|
if ((this.ackCredits.get(id) || 0) < target) return;
|