@coclaw/openclaw-coclaw 0.2.4 → 0.3.1
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/index.js +8 -4
- package/package.json +1 -1
- package/src/realtime-bridge.js +60 -39
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { registerCoclawCli } from './src/cli-registrar.js';
|
|
|
3
3
|
import { resolveErrorMessage } from './src/common/errors.js';
|
|
4
4
|
import { notBound, bindOk, unbindOk } from './src/common/messages.js';
|
|
5
5
|
import { coclawChannelPlugin } from './src/channel-plugin.js';
|
|
6
|
-
import { refreshRealtimeBridge, startRealtimeBridge, stopRealtimeBridge } from './src/realtime-bridge.js';
|
|
6
|
+
import { ensureAgentSession, refreshRealtimeBridge, startRealtimeBridge, stopRealtimeBridge } from './src/realtime-bridge.js';
|
|
7
7
|
import { setRuntime } from './src/runtime.js';
|
|
8
8
|
import { createSessionManager } from './src/session-manager/manager.js';
|
|
9
9
|
import { AutoUpgradeScheduler } from './src/auto-upgrade/updater.js';
|
|
@@ -70,7 +70,7 @@ const plugin = {
|
|
|
70
70
|
|
|
71
71
|
api.registerGatewayMethod('coclaw.refreshBridge', async ({ respond }) => {
|
|
72
72
|
try {
|
|
73
|
-
await refreshRealtimeBridge();
|
|
73
|
+
await refreshRealtimeBridge({ logger, pluginConfig: api.pluginConfig });
|
|
74
74
|
respond(true, { status: 'refreshed' });
|
|
75
75
|
}
|
|
76
76
|
catch (err) {
|
|
@@ -88,8 +88,12 @@ const plugin = {
|
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
api.registerGatewayMethod('nativeui.sessions.listAll', ({ params, respond }) => {
|
|
91
|
+
api.registerGatewayMethod('nativeui.sessions.listAll', async ({ params, respond }) => {
|
|
92
92
|
try {
|
|
93
|
+
const agentId = params?.agentId?.trim?.() || 'main';
|
|
94
|
+
// best-effort ensure:失败不阻断 listAll
|
|
95
|
+
try { await ensureAgentSession(agentId); }
|
|
96
|
+
catch {}
|
|
93
97
|
respond(true, manager.listAll(params ?? {}));
|
|
94
98
|
}
|
|
95
99
|
catch (err) {
|
|
@@ -144,7 +148,7 @@ const plugin = {
|
|
|
144
148
|
code: positionals[0],
|
|
145
149
|
serverUrl,
|
|
146
150
|
});
|
|
147
|
-
await refreshRealtimeBridge();
|
|
151
|
+
await refreshRealtimeBridge({ logger, pluginConfig: api.pluginConfig });
|
|
148
152
|
return { text: bindOk(result) };
|
|
149
153
|
}
|
|
150
154
|
|
package/package.json
CHANGED
package/src/realtime-bridge.js
CHANGED
|
@@ -90,8 +90,6 @@ export class RealtimeBridge {
|
|
|
90
90
|
this.gatewayConnectReqId = null;
|
|
91
91
|
this.gatewayRpcSeq = 0;
|
|
92
92
|
this.gatewayPendingRequests = new Map();
|
|
93
|
-
this.mainSessionEnsurePromise = null;
|
|
94
|
-
this.mainSessionEnsured = false;
|
|
95
93
|
this.logger = console;
|
|
96
94
|
this.pluginConfig = {};
|
|
97
95
|
this.intentionallyClosed = false;
|
|
@@ -248,45 +246,58 @@ export class RealtimeBridge {
|
|
|
248
246
|
});
|
|
249
247
|
}
|
|
250
248
|
|
|
251
|
-
|
|
252
|
-
|
|
249
|
+
/**
|
|
250
|
+
* 确保指定 agent 的主 session 存在(sessions.resolve + 条件 sessions.reset)
|
|
251
|
+
* @param {string} [agentId] - agent ID,默认 'main'
|
|
252
|
+
* @returns {Promise<{ok: boolean, state?: string, error?: string}>}
|
|
253
|
+
*/
|
|
254
|
+
async ensureAgentSession(agentId) {
|
|
255
|
+
const aid = typeof agentId === 'string' && agentId.trim() ? agentId.trim() : 'main';
|
|
256
|
+
const key = `agent:${aid}:main`;
|
|
257
|
+
const resolved = await this.__gatewayRpc('sessions.resolve', { key }, { timeoutMs: 2000 });
|
|
258
|
+
if (resolved?.ok === true) {
|
|
259
|
+
this.__logDebug(`ensure agent session: ready agentId=${aid}`);
|
|
253
260
|
return { ok: true, state: 'ready' };
|
|
254
261
|
}
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
return
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
262
|
+
// 仅当网关真实响应 "不存在" 时才创建;超时/网关未就绪等瞬态错误不触发 reset
|
|
263
|
+
if (!resolved?.response) {
|
|
264
|
+
return { ok: false, error: resolved?.error ?? 'resolve_transient_failure' };
|
|
265
|
+
}
|
|
266
|
+
// session key 不存在,通过 sessions.reset 创建
|
|
267
|
+
const reset = await this.__gatewayRpc('sessions.reset', { key, reason: 'new' }, { timeoutMs: 2500 });
|
|
268
|
+
if (reset?.ok !== true) {
|
|
269
|
+
return { ok: false, error: reset?.error ?? 'sessions_reset_failed' };
|
|
270
|
+
}
|
|
271
|
+
this.__logDebug(`ensure agent session: created agentId=${aid}`);
|
|
272
|
+
return { ok: true, state: 'created' };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async __ensureAllAgentSessions() {
|
|
276
|
+
try {
|
|
277
|
+
const listResult = await this.__gatewayRpc('agents.list', {}, { timeoutMs: 3000 });
|
|
278
|
+
let agentIds = ['main'];
|
|
279
|
+
if (listResult?.ok === true && Array.isArray(listResult?.response?.payload?.agents)) {
|
|
280
|
+
const ids = listResult.response.payload.agents
|
|
281
|
+
.map((a) => a?.id)
|
|
282
|
+
.filter((id) => typeof id === 'string' && id.trim());
|
|
283
|
+
if (ids.length > 0) agentIds = ids;
|
|
271
284
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (reset?.ok !== true) {
|
|
275
|
-
return { ok: false, error: reset?.error ?? 'sessions_reset_failed' };
|
|
285
|
+
else {
|
|
286
|
+
this.logger.warn?.(`[coclaw] agents.list failed, falling back to main: ${listResult?.error ?? 'unknown'}`);
|
|
276
287
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
this.logger.warn?.(`[coclaw] ensure
|
|
288
|
+
const results = await Promise.allSettled(
|
|
289
|
+
agentIds.map((id) => this.ensureAgentSession(id)),
|
|
290
|
+
);
|
|
291
|
+
for (let i = 0; i < results.length; i++) {
|
|
292
|
+
const r = results[i];
|
|
293
|
+
if (r.status === 'fulfilled' && r.value?.ok) continue;
|
|
294
|
+
const err = r.status === 'fulfilled' ? r.value?.error : String(r.reason);
|
|
295
|
+
this.logger.warn?.(`[coclaw] ensure agent session failed: agentId=${agentIds[i]} error=${err ?? 'unknown'}`);
|
|
285
296
|
}
|
|
286
|
-
return result;
|
|
287
297
|
}
|
|
288
|
-
|
|
289
|
-
|
|
298
|
+
/* c8 ignore next 3 -- 防御性兜底,__gatewayRpc 内部已有完整错误处理 */
|
|
299
|
+
catch (err) {
|
|
300
|
+
this.logger.warn?.(`[coclaw] ensureAllAgentSessions unexpected error: ${String(err?.message ?? err)}`);
|
|
290
301
|
}
|
|
291
302
|
}
|
|
292
303
|
|
|
@@ -395,7 +406,7 @@ export class RealtimeBridge {
|
|
|
395
406
|
this.gatewayReady = true;
|
|
396
407
|
this.__logDebug(`gateway connect ok <- id=${payload.id}`);
|
|
397
408
|
this.gatewayConnectReqId = null;
|
|
398
|
-
void this.
|
|
409
|
+
void this.__ensureAllAgentSessions();
|
|
399
410
|
}
|
|
400
411
|
else {
|
|
401
412
|
this.gatewayReady = false;
|
|
@@ -672,8 +683,6 @@ export class RealtimeBridge {
|
|
|
672
683
|
|
|
673
684
|
async stop() {
|
|
674
685
|
this.started = false;
|
|
675
|
-
this.mainSessionEnsured = false;
|
|
676
|
-
this.mainSessionEnsurePromise = null;
|
|
677
686
|
this.__clearServerHeartbeat();
|
|
678
687
|
this.__clearConnectTimer();
|
|
679
688
|
if (this.reconnectTimer) {
|
|
@@ -712,8 +721,12 @@ export async function startRealtimeBridge(opts) {
|
|
|
712
721
|
await singleton.start(opts);
|
|
713
722
|
}
|
|
714
723
|
|
|
715
|
-
export async function refreshRealtimeBridge() {
|
|
724
|
+
export async function refreshRealtimeBridge(opts) {
|
|
716
725
|
if (!singleton) {
|
|
726
|
+
// stop 后 singleton 被清除,需重新创建(如 bind 后重连)
|
|
727
|
+
if (opts) {
|
|
728
|
+
await startRealtimeBridge(opts);
|
|
729
|
+
}
|
|
717
730
|
return;
|
|
718
731
|
}
|
|
719
732
|
await singleton.refresh();
|
|
@@ -724,4 +737,12 @@ export async function stopRealtimeBridge() {
|
|
|
724
737
|
return;
|
|
725
738
|
}
|
|
726
739
|
await singleton.stop();
|
|
740
|
+
singleton = null;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
export async function ensureAgentSession(agentId) {
|
|
744
|
+
if (!singleton) {
|
|
745
|
+
return { ok: false, error: 'bridge_not_started' };
|
|
746
|
+
}
|
|
747
|
+
return singleton.ensureAgentSession(agentId);
|
|
727
748
|
}
|