@coclaw/openclaw-coclaw 0.11.2 → 0.11.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.
package/package.json
CHANGED
package/src/cli-registrar.js
CHANGED
|
@@ -41,7 +41,7 @@ async function restartGatewayProcess(spawnFn) {
|
|
|
41
41
|
}
|
|
42
42
|
/* c8 ignore stop */
|
|
43
43
|
|
|
44
|
-
// bind/unbind/enroll 的 RPC
|
|
44
|
+
// bind/unbind/enroll 的 RPC 超时(覆盖 openclaw gateway call 默认 10s)
|
|
45
45
|
// 卡点是 gateway ↔ server 的网络通信,bind 最多两次(先解绑再绑定)
|
|
46
46
|
const RPC_TIMEOUT_MS = 30_000;
|
|
47
47
|
|
|
@@ -27,6 +27,7 @@ export function escapeJsonForCmd(json) {
|
|
|
27
27
|
* 2. 监听 stdout,解析 JSON 输出判断 RPC 成功/失败
|
|
28
28
|
* 3. 检测到完整 JSON 后启动 KILL_DELAY_MS grace period 等待自然退出
|
|
29
29
|
* 4. 总超时默认 NOTIFY_TIMEOUT_MS(注册 CLI 路径覆盖为 30s)
|
|
30
|
+
* 同时通过 `--timeout` 传递给子进程,确保内外层超时一致
|
|
30
31
|
* 5. 无论成功失败,最终都主动 kill 子进程
|
|
31
32
|
*
|
|
32
33
|
* grace period 设计:openclaw 进程因 WS handle 滞留可能 10s+ 才退出,
|
|
@@ -55,6 +56,10 @@ export function callGatewayMethod(method, spawnFn, opts) {
|
|
|
55
56
|
try {
|
|
56
57
|
const isWin = opts?.isWin ?? IS_WIN;
|
|
57
58
|
const args = ['gateway', 'call', method, '--json'];
|
|
59
|
+
// 将超时传递给 openclaw gateway call(默认 10s),避免内外层超时不一致
|
|
60
|
+
if (opts?.timeoutMs) {
|
|
61
|
+
args.push('--timeout', String(opts.timeoutMs));
|
|
62
|
+
}
|
|
58
63
|
if (opts?.params) {
|
|
59
64
|
const json = JSON.stringify(opts.params);
|
|
60
65
|
// Windows 需 shell 解析 .cmd → 必须转义 JSON;非 Windows 不经 shell,直传
|
package/src/realtime-bridge.js
CHANGED
|
@@ -941,11 +941,9 @@ export class RealtimeBridge {
|
|
|
941
941
|
.then((v) => { this.__pluginVersion = v; })
|
|
942
942
|
.catch(() => { this.__pluginVersion = 'unknown'; }),
|
|
943
943
|
]);
|
|
944
|
-
// 竞态保护:若 preload 期间 stop()
|
|
944
|
+
// 竞态保护:若 preload 期间 stop() 已执行,不再赋值,直接返回。
|
|
945
|
+
// 不调 cleanup()——与 stop() 策略一致,native threads 保持活跃供后续复用。
|
|
945
946
|
if (!this.started) {
|
|
946
|
-
if (preloadResult.cleanup) {
|
|
947
|
-
try { preloadResult.cleanup(); } catch {}
|
|
948
|
-
}
|
|
949
947
|
return;
|
|
950
948
|
}
|
|
951
949
|
this.__ndcPreloadResult = preloadResult;
|
|
@@ -979,15 +977,12 @@ export class RealtimeBridge {
|
|
|
979
977
|
this.webrtcPeer = null;
|
|
980
978
|
this.__webrtcPeerReady = null;
|
|
981
979
|
}
|
|
982
|
-
//
|
|
983
|
-
//
|
|
984
|
-
//
|
|
985
|
-
//
|
|
986
|
-
//
|
|
987
|
-
|
|
988
|
-
try { this.__ndcCleanup(); }
|
|
989
|
-
catch (err) { remoteLog(`ndc.cleanup-failed error=${err?.message}`); }
|
|
990
|
-
}
|
|
980
|
+
// 不在 stop() 中调用 ndc.cleanup():
|
|
981
|
+
// cleanup() 是同步 native 调用,需 join native threads,耗时 10s+,
|
|
982
|
+
// 会阻塞事件循环导致 RPC handler 超时。
|
|
983
|
+
// gateway 是长驻进程,native threads 保持活跃即可;
|
|
984
|
+
// 下次 start() 重新 import(ESM 缓存命中)可直接复用。
|
|
985
|
+
// 进程退出时 OS 会回收所有资源。
|
|
991
986
|
this.__ndcCleanup = null;
|
|
992
987
|
this.__ndcPreloadResult = null;
|
|
993
988
|
if (this.__fileHandler) {
|
|
@@ -60,6 +60,33 @@ export function defaultResolvePaths(platformKey, pluginRoot) {
|
|
|
60
60
|
return { src, dest, destDir };
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* ndc polyfill 的 RTCPeerConnection 将 iceServers 的 username:credential 直接拼入 URL,
|
|
65
|
+
* 但 TURN REST API 的 username 格式为 `timestamp:identity`(含冒号),
|
|
66
|
+
* 导致 libdatachannel 的 URL parser 截断 username。
|
|
67
|
+
* 此 wrapper 在传入 polyfill 前对 username/credential 做 percent-encoding 规避该问题。
|
|
68
|
+
*/
|
|
69
|
+
function wrapNdcCredentials(NativeRTC) {
|
|
70
|
+
return class extends NativeRTC {
|
|
71
|
+
constructor(config = {}) {
|
|
72
|
+
if (config?.iceServers) {
|
|
73
|
+
config = {
|
|
74
|
+
...config,
|
|
75
|
+
iceServers: config.iceServers.map(s => {
|
|
76
|
+
if (!s.username && !s.credential) return s;
|
|
77
|
+
return {
|
|
78
|
+
...s,
|
|
79
|
+
username: s.username ? encodeURIComponent(s.username) : s.username,
|
|
80
|
+
credential: s.credential ? encodeURIComponent(s.credential) : s.credential,
|
|
81
|
+
};
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
super(config);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
63
90
|
/**
|
|
64
91
|
* 预加载 WebRTC 实现:优先 node-datachannel,失败回退 werift,全部失败返回 null。
|
|
65
92
|
*
|
|
@@ -160,7 +187,7 @@ export async function preloadNdc(deps = {}) {
|
|
|
160
187
|
// 当前由 RealtimeBridge.stop() 负责调用。若 gateway 被 SIGKILL 强杀则无法执行,
|
|
161
188
|
// 但 OS 会回收所有资源。若 OpenClaw 提供了优雅终止钩子,应在钩子中也调用 cleanup。
|
|
162
189
|
log(`ndc.loaded platform=${platformKey}`);
|
|
163
|
-
return { PeerConnection: RTCPeerConnection, cleanup, impl: 'ndc' };
|
|
190
|
+
return { PeerConnection: wrapNdcCredentials(RTCPeerConnection), cleanup, impl: 'ndc' };
|
|
164
191
|
} catch (err) {
|
|
165
192
|
// resolvePaths 或其他未预期异常的兜底
|
|
166
193
|
log(`ndc.fallback reason=unexpected error=${err.message}`);
|