openclaw-quiubo 2.6.9 → 2.6.13
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/dist/index.js
CHANGED
|
@@ -12470,6 +12470,11 @@ var RealtimeGateway = class {
|
|
|
12470
12470
|
pollTimer = null;
|
|
12471
12471
|
polling = false;
|
|
12472
12472
|
cursors = /* @__PURE__ */ new Map();
|
|
12473
|
+
/** Message IDs already dispatched (Pusher + poll dedup). Capped to prevent unbounded growth. */
|
|
12474
|
+
dispatched = /* @__PURE__ */ new Set();
|
|
12475
|
+
// Cached directory group IDs from listAgentGroups (refreshed every 60s)
|
|
12476
|
+
agentGroupsCache = null;
|
|
12477
|
+
AGENT_GROUPS_TTL = 6e4;
|
|
12473
12478
|
// Bot config cache (per group)
|
|
12474
12479
|
botConfigCache = /* @__PURE__ */ new Map();
|
|
12475
12480
|
// Owner takeover suppression: groupId → suppressedUntil timestamp
|
|
@@ -12503,6 +12508,13 @@ var RealtimeGateway = class {
|
|
|
12503
12508
|
setBotConfig(groupId, config) {
|
|
12504
12509
|
this.botConfigCache.set(groupId, config);
|
|
12505
12510
|
}
|
|
12511
|
+
/** Track a dispatched message ID (capped at 500 to prevent unbounded growth) */
|
|
12512
|
+
markDispatched(messageId) {
|
|
12513
|
+
this.dispatched.add(messageId);
|
|
12514
|
+
if (this.dispatched.size > 500) {
|
|
12515
|
+
this.dispatched.clear();
|
|
12516
|
+
}
|
|
12517
|
+
}
|
|
12506
12518
|
/** Get bot config for a group (for outbound scope checks) */
|
|
12507
12519
|
getBotConfig(groupId) {
|
|
12508
12520
|
return this.botConfigCache.get(groupId);
|
|
@@ -12599,13 +12611,13 @@ var RealtimeGateway = class {
|
|
|
12599
12611
|
*/
|
|
12600
12612
|
async start() {
|
|
12601
12613
|
this.stopped = false;
|
|
12602
|
-
|
|
12614
|
+
this.log.info?.(`Gateway starting: botIdentityId=${this.botIdentityId} pusher=${!!this.pusherConfig} agentId=${this.agentId}`);
|
|
12603
12615
|
this.startPolling();
|
|
12604
12616
|
if (this.agentId) this.startHeartbeat();
|
|
12605
12617
|
if (this.pusherConfig) {
|
|
12606
12618
|
await this.startPusher();
|
|
12607
12619
|
} else {
|
|
12608
|
-
|
|
12620
|
+
this.log.info?.("No Pusher config \u2014 polling only");
|
|
12609
12621
|
}
|
|
12610
12622
|
}
|
|
12611
12623
|
/**
|
|
@@ -12639,8 +12651,7 @@ var RealtimeGateway = class {
|
|
|
12639
12651
|
// ── Pusher ──────────────────────────────────────────────────────
|
|
12640
12652
|
async startPusher() {
|
|
12641
12653
|
if (!this.pusherConfig) return;
|
|
12642
|
-
|
|
12643
|
-
this.log.info?.(`[gateway] startPusher called for botIdentityId=${this.botIdentityId}`);
|
|
12654
|
+
this.log.info?.(`Pusher connecting: key=${this.pusherConfig.key} cluster=${this.pusherConfig.cluster}`);
|
|
12644
12655
|
const { key, cluster } = this.pusherConfig;
|
|
12645
12656
|
const apiClient = this.client;
|
|
12646
12657
|
const botIdentityId = this.botIdentityId;
|
|
@@ -12661,12 +12672,10 @@ var RealtimeGateway = class {
|
|
|
12661
12672
|
})
|
|
12662
12673
|
});
|
|
12663
12674
|
this.pusherClient.connection.bind("state_change", (states) => {
|
|
12664
|
-
console.log(`[gw:pusher] state: ${states.previous} \u2192 ${states.current}`);
|
|
12665
12675
|
log.info?.(`Pusher state: ${states.previous} \u2192 ${states.current}`);
|
|
12666
12676
|
});
|
|
12667
12677
|
this.pusherClient.connection.bind("connected", () => {
|
|
12668
12678
|
this.pusherConnected = true;
|
|
12669
|
-
console.log("[gw:pusher] connected");
|
|
12670
12679
|
log.info?.("Pusher connected (polling continues as safety net)");
|
|
12671
12680
|
if (this.agentId) this.startHeartbeat();
|
|
12672
12681
|
});
|
|
@@ -12757,17 +12766,19 @@ var RealtimeGateway = class {
|
|
|
12757
12766
|
agentDisplayName: this.agentDisplayName ?? void 0,
|
|
12758
12767
|
securityMode: data.securityMode
|
|
12759
12768
|
});
|
|
12769
|
+
this.agentGroupsCache = null;
|
|
12760
12770
|
});
|
|
12761
12771
|
channel.bind("agent:group-removed", (data) => {
|
|
12762
12772
|
log.info?.(`agent:group-removed for group ${data.groupId}`);
|
|
12763
12773
|
this.botConfigCache.delete(data.groupId);
|
|
12774
|
+
this.agentGroupsCache = null;
|
|
12764
12775
|
});
|
|
12765
12776
|
channel.bind_global((eventName, data) => {
|
|
12766
|
-
|
|
12777
|
+
if (eventName.startsWith("pusher:") || eventName.startsWith("pusher_")) return;
|
|
12778
|
+
log.debug?.(`Pusher event: ${eventName} data=${JSON.stringify(data).slice(0, 200)}`);
|
|
12767
12779
|
});
|
|
12768
12780
|
channel.bind("new-group-message", (data) => {
|
|
12769
|
-
|
|
12770
|
-
log.info?.(`Pusher event: messageId=${data.messageId} groupId=${data.groupId} sender=${data.senderUsername} plaintext=${!!data.plaintext} ciphertext=${!!data.ciphertext} botId=${this.botIdentityId}`);
|
|
12781
|
+
log.info?.(`new-group-message: msg=${data.messageId} group=${data.groupId} sender=${data.senderUsername} plaintext=${!!data.plaintext} ciphertext=${!!data.ciphertext}`);
|
|
12771
12782
|
if (data.senderId === this.botIdentityId) return;
|
|
12772
12783
|
this.shouldProcessMessage(data.groupId, data.senderId, {
|
|
12773
12784
|
fromPusher: true,
|
|
@@ -12781,6 +12792,7 @@ var RealtimeGateway = class {
|
|
|
12781
12792
|
if (!shouldProcess) return;
|
|
12782
12793
|
if (data.plaintext) {
|
|
12783
12794
|
this.cursors.set(data.groupId, data.messageId);
|
|
12795
|
+
this.markDispatched(data.messageId);
|
|
12784
12796
|
Promise.resolve(this.onMessage({
|
|
12785
12797
|
messageId: data.messageId,
|
|
12786
12798
|
groupId: data.groupId,
|
|
@@ -12808,6 +12820,7 @@ var RealtimeGateway = class {
|
|
|
12808
12820
|
const plaintext = decryptGroupMessage(data.ciphertext, cached.key);
|
|
12809
12821
|
log.info?.(`[e2ee:pusher] group=${data.groupId} decrypted OK: ${plaintext?.length} chars \u2014 routing to onMessage`);
|
|
12810
12822
|
this.cursors.set(data.groupId, data.messageId);
|
|
12823
|
+
this.markDispatched(data.messageId);
|
|
12811
12824
|
return Promise.resolve(this.onMessage({
|
|
12812
12825
|
messageId: data.messageId,
|
|
12813
12826
|
groupId: data.groupId,
|
|
@@ -12859,6 +12872,8 @@ var RealtimeGateway = class {
|
|
|
12859
12872
|
this.log.warn?.(`Message ${messageId} has no plaintext`);
|
|
12860
12873
|
return;
|
|
12861
12874
|
}
|
|
12875
|
+
if (this.dispatched.has(msg.id)) return;
|
|
12876
|
+
this.markDispatched(msg.id);
|
|
12862
12877
|
await this.onMessage({
|
|
12863
12878
|
messageId: msg.id,
|
|
12864
12879
|
groupId,
|
|
@@ -12880,8 +12895,7 @@ var RealtimeGateway = class {
|
|
|
12880
12895
|
// ── Polling (fallback) ──────────────────────────────────────────
|
|
12881
12896
|
startPolling() {
|
|
12882
12897
|
if (this.pollTimer) return;
|
|
12883
|
-
|
|
12884
|
-
this.log.info?.(`[gateway] startPolling called for botIdentityId=${this.botIdentityId}`);
|
|
12898
|
+
this.log.info?.(`Polling started: interval=${this.pollIntervalMs}ms`);
|
|
12885
12899
|
this.poll();
|
|
12886
12900
|
this.pollTimer = setInterval(() => this.poll(), this.pollIntervalMs);
|
|
12887
12901
|
}
|
|
@@ -12891,13 +12905,61 @@ var RealtimeGateway = class {
|
|
|
12891
12905
|
this.pollTimer = null;
|
|
12892
12906
|
}
|
|
12893
12907
|
}
|
|
12908
|
+
/**
|
|
12909
|
+
* Get directory group IDs, refreshing from API every AGENT_GROUPS_TTL ms.
|
|
12910
|
+
* Falls back to botConfigCache if the API call fails.
|
|
12911
|
+
* Pusher agent:group-added/removed events also invalidate the cache.
|
|
12912
|
+
*/
|
|
12913
|
+
async getDirectoryGroupIds() {
|
|
12914
|
+
if (!this.agentId) {
|
|
12915
|
+
const ids = /* @__PURE__ */ new Set();
|
|
12916
|
+
for (const [gid, config] of this.botConfigCache) {
|
|
12917
|
+
if (config.source === "directory" && config.enabled) ids.add(gid);
|
|
12918
|
+
}
|
|
12919
|
+
return ids;
|
|
12920
|
+
}
|
|
12921
|
+
const now = Date.now();
|
|
12922
|
+
if (this.agentGroupsCache && now - this.agentGroupsCache.fetchedAt < this.AGENT_GROUPS_TTL) {
|
|
12923
|
+
return this.agentGroupsCache.groupIds;
|
|
12924
|
+
}
|
|
12925
|
+
try {
|
|
12926
|
+
const { groups: agentGroups } = await this.client.listAgentGroups(this.agentId);
|
|
12927
|
+
const ids = /* @__PURE__ */ new Set();
|
|
12928
|
+
for (const ag of agentGroups) {
|
|
12929
|
+
if (ag.source === "directory" && (ag.enabled ?? true)) {
|
|
12930
|
+
ids.add(ag.groupId);
|
|
12931
|
+
if (!this.botConfigCache.has(ag.groupId)) {
|
|
12932
|
+
this.botConfigCache.set(ag.groupId, {
|
|
12933
|
+
enabled: ag.enabled ?? true,
|
|
12934
|
+
suppressionMinutes: 10,
|
|
12935
|
+
ownerIdentityId: "",
|
|
12936
|
+
groupType: ag.groupType ?? "standard",
|
|
12937
|
+
source: "directory",
|
|
12938
|
+
grantedScopes: ag.grantedScopes,
|
|
12939
|
+
agentDisplayName: ag.agentDisplayName ?? this.agentDisplayName ?? void 0,
|
|
12940
|
+
securityMode: ag.securityMode
|
|
12941
|
+
});
|
|
12942
|
+
}
|
|
12943
|
+
}
|
|
12944
|
+
}
|
|
12945
|
+
this.agentGroupsCache = { groupIds: ids, fetchedAt: now };
|
|
12946
|
+
return ids;
|
|
12947
|
+
} catch (err) {
|
|
12948
|
+
this.log.warn?.(`listAgentGroups failed: ${err}, using cache`);
|
|
12949
|
+
if (this.agentGroupsCache) return this.agentGroupsCache.groupIds;
|
|
12950
|
+
const ids = /* @__PURE__ */ new Set();
|
|
12951
|
+
for (const [gid, config] of this.botConfigCache) {
|
|
12952
|
+
if (config.source === "directory" && config.enabled) ids.add(gid);
|
|
12953
|
+
}
|
|
12954
|
+
return ids;
|
|
12955
|
+
}
|
|
12956
|
+
}
|
|
12894
12957
|
async poll() {
|
|
12895
12958
|
if (this.polling) return;
|
|
12896
12959
|
this.polling = true;
|
|
12897
12960
|
try {
|
|
12898
12961
|
this.clearStaleSuppression();
|
|
12899
12962
|
const { groups } = await this.client.listGroups(this.botIdentityId);
|
|
12900
|
-
console.log(`[gw:poll] listGroups returned ${groups.length} groups, botConfigCache has ${this.botConfigCache.size} entries`);
|
|
12901
12963
|
for (const group of groups) {
|
|
12902
12964
|
if (!this.botConfigCache.has(group.id) && group.settings) {
|
|
12903
12965
|
const botSettings = group.settings?.bot ?? {};
|
|
@@ -12911,11 +12973,9 @@ var RealtimeGateway = class {
|
|
|
12911
12973
|
}
|
|
12912
12974
|
}
|
|
12913
12975
|
const groupIds = new Set(groups.map((g) => g.id));
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
}
|
|
12918
|
-
}
|
|
12976
|
+
const directoryIds = await this.getDirectoryGroupIds();
|
|
12977
|
+
for (const gid of directoryIds) groupIds.add(gid);
|
|
12978
|
+
this.log.debug?.(`Poll cycle: partner=${groups.length} directory=${directoryIds.size} total=${groupIds.size}`);
|
|
12919
12979
|
await Promise.allSettled(
|
|
12920
12980
|
[...groupIds].map((gid) => this.pollGroup(gid))
|
|
12921
12981
|
);
|
|
@@ -12928,7 +12988,6 @@ var RealtimeGateway = class {
|
|
|
12928
12988
|
async pollGroup(groupId) {
|
|
12929
12989
|
try {
|
|
12930
12990
|
const cursor = this.cursors.get(groupId);
|
|
12931
|
-
console.log(`[gw:pollGroup] group=${groupId} cursor=${cursor ?? "none"}`);
|
|
12932
12991
|
const { messages } = await this.client.listMessages(groupId, cursor);
|
|
12933
12992
|
if (messages.length === 0) return;
|
|
12934
12993
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -12952,12 +13011,10 @@ var RealtimeGateway = class {
|
|
|
12952
13011
|
}
|
|
12953
13012
|
if (!plaintext) continue;
|
|
12954
13013
|
const shouldProcess = await this.shouldProcessMessage(groupId, msg.senderIdentityId, { plaintext });
|
|
12955
|
-
if (!shouldProcess)
|
|
12956
|
-
|
|
12957
|
-
continue;
|
|
12958
|
-
}
|
|
13014
|
+
if (!shouldProcess) continue;
|
|
13015
|
+
if (this.dispatched.has(msg.id)) continue;
|
|
12959
13016
|
try {
|
|
12960
|
-
|
|
13017
|
+
this.markDispatched(msg.id);
|
|
12961
13018
|
await this.onMessage({
|
|
12962
13019
|
messageId: msg.id,
|
|
12963
13020
|
groupId,
|
|
@@ -13415,7 +13472,6 @@ var quiuboPlugin = {
|
|
|
13415
13472
|
accounts.set(accountId, account);
|
|
13416
13473
|
}
|
|
13417
13474
|
const groupId = resolveOutboundGroupId(ctx);
|
|
13418
|
-
console.log(`[delivery:sendText] ctx.to=${ctx.to}, sessionKey=${ctx.SessionKey ?? ctx.target?.raw?.SessionKey}, conversationLabel=${ctx.target?.raw?.ConversationLabel}, resolved groupId=${groupId}, text=${text?.length ?? 0} chars`);
|
|
13419
13475
|
if (!groupId) {
|
|
13420
13476
|
return { ok: false, error: "No groupId in outbound context" };
|
|
13421
13477
|
}
|
|
@@ -13424,7 +13480,6 @@ var quiuboPlugin = {
|
|
|
13424
13480
|
return { ok: false, error: "No botIdentityId configured" };
|
|
13425
13481
|
}
|
|
13426
13482
|
try {
|
|
13427
|
-
console.log(`[delivery:sendText] calling client.sendMessage(${groupId}, sender=${senderId})`);
|
|
13428
13483
|
await client.sendMessage(groupId, {
|
|
13429
13484
|
senderIdentityId: senderId,
|
|
13430
13485
|
plaintext: text,
|
|
@@ -13443,7 +13498,6 @@ var quiuboPlugin = {
|
|
|
13443
13498
|
if (ctx.mediaUrl) urls.push(ctx.mediaUrl);
|
|
13444
13499
|
if (Array.isArray(ctx.mediaUrls)) urls.push(...ctx.mediaUrls);
|
|
13445
13500
|
const mdAttachments = await readMdAttachments(urls, "agent", void 0, "sendMedia");
|
|
13446
|
-
console.log(`[delivery:sendMedia] urls=${urls.length}, mdAttachments=${mdAttachments.length} [${mdAttachments.map((a) => a.filename).join(", ")}], media=${Array.isArray(ctx.media) ? ctx.media.length : "no"}`);
|
|
13447
13501
|
const plaintext = text || (mdAttachments.length === 0 ? "[Media attachment]" : "");
|
|
13448
13502
|
const accountId = ctx.accountId ?? DEFAULT_ACCOUNT_ID;
|
|
13449
13503
|
let client = clients.get(accountId);
|
|
@@ -13460,7 +13514,6 @@ var quiuboPlugin = {
|
|
|
13460
13514
|
accounts.set(accountId, account);
|
|
13461
13515
|
}
|
|
13462
13516
|
const groupId = resolveOutboundGroupId(ctx);
|
|
13463
|
-
console.log(`[delivery:sendMedia] ctx.to=${ctx.to}, sessionKey=${ctx.SessionKey ?? ctx.target?.raw?.SessionKey}, conversationLabel=${ctx.target?.raw?.ConversationLabel}, resolved groupId=${groupId}`);
|
|
13464
13517
|
if (!groupId) {
|
|
13465
13518
|
return { ok: false, error: "No groupId in outbound context" };
|
|
13466
13519
|
}
|
|
@@ -13469,7 +13522,6 @@ var quiuboPlugin = {
|
|
|
13469
13522
|
return { ok: false, error: "No botIdentityId configured" };
|
|
13470
13523
|
}
|
|
13471
13524
|
try {
|
|
13472
|
-
console.log(`[delivery:sendMedia] calling client.sendMessage(${groupId}, sender=${senderId})`);
|
|
13473
13525
|
await client.sendMessage(groupId, {
|
|
13474
13526
|
senderIdentityId: senderId,
|
|
13475
13527
|
plaintext,
|
|
@@ -13569,10 +13621,10 @@ var quiuboPlugin = {
|
|
|
13569
13621
|
log?.warn?.(`[${accountId}] Quiubo: auto-provision failed (non-fatal): ${provisionError}`);
|
|
13570
13622
|
}
|
|
13571
13623
|
const gatewayLog = log ? {
|
|
13572
|
-
debug: (...args) => log.debug?.(
|
|
13573
|
-
info: (...args) => log.info?.(
|
|
13574
|
-
warn: (...args) => log.warn?.(
|
|
13575
|
-
error: (...args) => log.error?.(
|
|
13624
|
+
debug: (...args) => log.debug?.(`${args.join(" ")}`),
|
|
13625
|
+
info: (...args) => log.info?.(`${args.join(" ")}`),
|
|
13626
|
+
warn: (...args) => log.warn?.(`${args.join(" ")}`),
|
|
13627
|
+
error: (...args) => log.error?.(`${args.join(" ")}`)
|
|
13576
13628
|
} : void 0;
|
|
13577
13629
|
let resolvedAgentId;
|
|
13578
13630
|
let resolvedAgentDisplayName;
|
|
@@ -13860,7 +13912,6 @@ function resolveOutboundGroupId(ctx) {
|
|
|
13860
13912
|
}
|
|
13861
13913
|
const targetGroupId = ctx.target?.groupId ?? raw.groupId;
|
|
13862
13914
|
if (targetGroupId) return targetGroupId;
|
|
13863
|
-
console.log(`[delivery:resolveOutboundGroupId] no ConversationLabel/SessionKey/groupId found, ctx.to=${ctx.to} (not used \u2014 likely botIdentityId)`);
|
|
13864
13915
|
return void 0;
|
|
13865
13916
|
}
|
|
13866
13917
|
async function getActivityData(runtime2, log, agentId) {
|