@coclaw/openclaw-coclaw 0.15.0 → 0.16.0
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coclaw/openclaw-coclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "OpenClaw CoClaw channel plugin for remote chat",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"node-datachannel": "0.32.2",
|
|
63
|
-
"@coclaw/pion-node": "^0.1.
|
|
63
|
+
"@coclaw/pion-node": "^0.1.3",
|
|
64
64
|
"werift": "^0.19.0",
|
|
65
65
|
"ws": "^8.19.0"
|
|
66
66
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* updater-check.js — 版本检查
|
|
3
3
|
*
|
|
4
4
|
* 通过 `npm view` 查询 registry 最新版本,与本地 package.json 对比。
|
|
5
|
-
* 选择 npm view
|
|
5
|
+
* 选择 npm view 而非自己打 registry HTTP 接口,是因为它自动继承用户完整的
|
|
6
6
|
* npm 环境配置(registry 镜像、proxy、scoped registry、auth token 等),
|
|
7
7
|
* 避免自行解析多层 .npmrc 的复杂性。每小时一次的频率下进程启动开销可忽略。
|
|
8
8
|
*/
|
|
@@ -110,6 +110,27 @@ export class WebRtcPeer {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* 向指定 connId 的 rpc DC 单播一个 JSON 帧(不走 server 中转)。
|
|
115
|
+
* 若 session/DC 未就绪返回 false,由调用方决定是否重试。
|
|
116
|
+
* @param {string} connId
|
|
117
|
+
* @param {object} payload - 完整的 JSON 帧(通常是 { type: 'event', event, payload })
|
|
118
|
+
* @returns {boolean} true=已入队发送,false=未能发送(session 不存在 / DC 未 open)
|
|
119
|
+
*/
|
|
120
|
+
sendTo(connId, payload) {
|
|
121
|
+
const session = this.__sessions.get(connId);
|
|
122
|
+
if (!session) return false;
|
|
123
|
+
const q = session.rpcSendQueue;
|
|
124
|
+
if (!q || session.rpcChannel?.readyState !== 'open') return false;
|
|
125
|
+
try {
|
|
126
|
+
q.send(JSON.stringify(payload));
|
|
127
|
+
return true;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
this.__logDebug(`[${connId}] sendTo failed: ${err.message}`);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
113
134
|
async __handleOffer(msg) {
|
|
114
135
|
const connId = msg.fromConnId;
|
|
115
136
|
const isIceRestart = !!msg.payload?.iceRestart;
|
|
@@ -307,6 +328,9 @@ export class WebRtcPeer {
|
|
|
307
328
|
if (pair) {
|
|
308
329
|
this.__logNominatedPair(connId, pair);
|
|
309
330
|
}
|
|
331
|
+
// ICE restart 或初次选中都会触发;让出一次 CPU 后再单播 transport 信息。
|
|
332
|
+
// 签名去重保证 pair 不变时不会重复发送。
|
|
333
|
+
queueMicrotask(() => this.__sendPeerTransport(connId));
|
|
310
334
|
};
|
|
311
335
|
}
|
|
312
336
|
|
|
@@ -424,6 +448,12 @@ export class WebRtcPeer {
|
|
|
424
448
|
dc.onopen = () => {
|
|
425
449
|
this.__remoteLog(`dc.open conn=${connId} label=${dc.label}`);
|
|
426
450
|
this.logger.info?.(`${this.__rtcTag} [${connId}] DataChannel "${dc.label}" opened`);
|
|
451
|
+
// rpc DC 建立后,把本端 transport 信息单播给 UI。
|
|
452
|
+
// queueMicrotask 让出一次 CPU:确保 pion 侧 selectedCandidatePair setter 已 assign,
|
|
453
|
+
// 同时避免在 onopen 同步栈里触发可能的重入。
|
|
454
|
+
if (dc.label === 'rpc') {
|
|
455
|
+
queueMicrotask(() => this.__sendPeerTransport(connId));
|
|
456
|
+
}
|
|
427
457
|
};
|
|
428
458
|
dc.onclose = () => {
|
|
429
459
|
this.__remoteLog(`dc.closed conn=${connId} label=${dc.label}`);
|
|
@@ -481,12 +511,51 @@ export class WebRtcPeer {
|
|
|
481
511
|
}
|
|
482
512
|
|
|
483
513
|
__logNominatedPair(connId, pair) {
|
|
484
|
-
const
|
|
485
|
-
const
|
|
514
|
+
const l = pair.local, r = pair.remote;
|
|
515
|
+
const lProto = (l?.protocol ?? '?').toLowerCase();
|
|
516
|
+
const rProto = (r?.protocol ?? '?').toLowerCase();
|
|
517
|
+
const lRelay = l?.relayProtocol ? `(${String(l.relayProtocol).toLowerCase()})` : '';
|
|
518
|
+
const localInfo = `${l?.type ?? '?'}/${lProto}${lRelay} ${l?.address ?? l?.host ?? '?'}:${l?.port ?? '?'}`;
|
|
519
|
+
const remoteInfo = `${r?.type ?? '?'}/${rProto} ${r?.address ?? r?.host ?? '?'}:${r?.port ?? '?'}`;
|
|
486
520
|
this.__remoteLog(`rtc.ice-nominated conn=${connId} local=${localInfo} remote=${remoteInfo}`);
|
|
487
521
|
this.logger.info?.(`${this.__rtcTag} [${connId}] ICE nominated: local=${localInfo} remote=${remoteInfo}`);
|
|
488
522
|
}
|
|
489
523
|
|
|
524
|
+
/**
|
|
525
|
+
* 把当前 session 本端 candidate 的 transport 信息(type/protocol/relayProtocol)
|
|
526
|
+
* 通过 coclaw.rtc.peerTransport 事件单播给对应 UI。已内置签名去重,
|
|
527
|
+
* 同一签名不会重复发送;发送失败(DC 未 open)时回滚签名允许后续重试。
|
|
528
|
+
*
|
|
529
|
+
* @param {string} connId
|
|
530
|
+
*/
|
|
531
|
+
__sendPeerTransport(connId) {
|
|
532
|
+
const session = this.__sessions.get(connId);
|
|
533
|
+
if (!session) return;
|
|
534
|
+
const local = session.pc?.selectedCandidatePair?.local;
|
|
535
|
+
if (!local) return; // nominated pair 尚未产生
|
|
536
|
+
const payload = {
|
|
537
|
+
candidateType: local.type ?? 'unknown',
|
|
538
|
+
protocol: String(local.protocol ?? 'udp').toLowerCase(),
|
|
539
|
+
relayProtocol: local.relayProtocol
|
|
540
|
+
? String(local.relayProtocol).toLowerCase()
|
|
541
|
+
: null,
|
|
542
|
+
};
|
|
543
|
+
const sig = `${payload.candidateType}|${payload.protocol}|${payload.relayProtocol ?? ''}`;
|
|
544
|
+
if (session.__lastPeerTransportSig === sig) return;
|
|
545
|
+
session.__lastPeerTransportSig = sig;
|
|
546
|
+
const ok = this.sendTo(connId, {
|
|
547
|
+
type: 'event',
|
|
548
|
+
event: 'coclaw.rtc.peerTransport',
|
|
549
|
+
payload,
|
|
550
|
+
});
|
|
551
|
+
if (!ok) {
|
|
552
|
+
// DC 尚未 open,回滚签名以便 dc.onopen 再次触发时重发
|
|
553
|
+
session.__lastPeerTransportSig = null;
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
this.__remoteLog(`rtc.peer-transport conn=${connId} type=${payload.candidateType} proto=${payload.protocol} relay=${payload.relayProtocol ?? '-'}`);
|
|
557
|
+
}
|
|
558
|
+
|
|
490
559
|
__remoteLog(msg) {
|
|
491
560
|
remoteLog(this.__impl ? `${msg} rtc=${this.__impl}` : msg);
|
|
492
561
|
}
|