openclaw-quiubo 2.0.3 → 2.2.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/dist/index.js +197 -35
- package/dist/index.js.map +2 -2
- package/dist/src/api.d.ts +33 -0
- package/dist/src/api.d.ts.map +1 -1
- package/dist/src/channel.d.ts.map +1 -1
- package/dist/src/realtime-gateway.d.ts +17 -0
- package/dist/src/realtime-gateway.d.ts.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__
|
|
|
34
34
|
// ../../node_modules/.pnpm/pusher-js@8.4.0/node_modules/pusher-js/dist/node/pusher.js
|
|
35
35
|
var require_pusher = __commonJS({
|
|
36
36
|
"../../node_modules/.pnpm/pusher-js@8.4.0/node_modules/pusher-js/dist/node/pusher.js"(exports, module) {
|
|
37
|
+
"use strict";
|
|
37
38
|
module.exports = /******/
|
|
38
39
|
(function(modules) {
|
|
39
40
|
var installedModules = {};
|
|
@@ -9191,6 +9192,25 @@ var QuiuboApiClient = class {
|
|
|
9191
9192
|
return this.request("GET", `/agents/${agentId}/groups/${groupId}/encryption/epochs${query}`);
|
|
9192
9193
|
}
|
|
9193
9194
|
// ========================================================================
|
|
9195
|
+
// Group Agents
|
|
9196
|
+
// ========================================================================
|
|
9197
|
+
/**
|
|
9198
|
+
* List all groups for an agent (partner + directory)
|
|
9199
|
+
*/
|
|
9200
|
+
async listAgentGroups(agentId) {
|
|
9201
|
+
return this.request("GET", `/agents/${agentId}/groups`);
|
|
9202
|
+
}
|
|
9203
|
+
/**
|
|
9204
|
+
* Get group agent status (cache-miss fallback for directory groups)
|
|
9205
|
+
*/
|
|
9206
|
+
async getGroupAgent(agentId, groupId) {
|
|
9207
|
+
try {
|
|
9208
|
+
return await this.request("GET", `/agents/${agentId}/groups/${groupId}/agent-status`);
|
|
9209
|
+
} catch {
|
|
9210
|
+
return null;
|
|
9211
|
+
}
|
|
9212
|
+
}
|
|
9213
|
+
// ========================================================================
|
|
9194
9214
|
// Pusher Auth
|
|
9195
9215
|
// ========================================================================
|
|
9196
9216
|
/**
|
|
@@ -12459,6 +12479,9 @@ var RealtimeGateway = class {
|
|
|
12459
12479
|
// E2EE state
|
|
12460
12480
|
keyManager;
|
|
12461
12481
|
e2eeGrantedGroups;
|
|
12482
|
+
// Agent identity for mention matching
|
|
12483
|
+
agentName;
|
|
12484
|
+
agentDisplayName;
|
|
12462
12485
|
stopped = false;
|
|
12463
12486
|
constructor(opts) {
|
|
12464
12487
|
this.client = opts.client;
|
|
@@ -12472,11 +12495,17 @@ var RealtimeGateway = class {
|
|
|
12472
12495
|
this.agentId = opts.agentId ?? null;
|
|
12473
12496
|
this.keyManager = opts.keyManager ?? null;
|
|
12474
12497
|
this.e2eeGrantedGroups = opts.e2eeGrantedGroups ?? /* @__PURE__ */ new Set();
|
|
12498
|
+
this.agentName = opts.agentName ?? null;
|
|
12499
|
+
this.agentDisplayName = opts.agentDisplayName ?? null;
|
|
12475
12500
|
}
|
|
12476
12501
|
/** Set bot config for a group */
|
|
12477
12502
|
setBotConfig(groupId, config) {
|
|
12478
12503
|
this.botConfigCache.set(groupId, config);
|
|
12479
12504
|
}
|
|
12505
|
+
/** Get bot config for a group (for outbound scope checks) */
|
|
12506
|
+
getBotConfig(groupId) {
|
|
12507
|
+
return this.botConfigCache.get(groupId);
|
|
12508
|
+
}
|
|
12480
12509
|
/** Set the cache-miss callback */
|
|
12481
12510
|
setOnCacheMiss(fn) {
|
|
12482
12511
|
this.onCacheMiss = fn;
|
|
@@ -12491,6 +12520,20 @@ var RealtimeGateway = class {
|
|
|
12491
12520
|
}
|
|
12492
12521
|
}
|
|
12493
12522
|
}
|
|
12523
|
+
/**
|
|
12524
|
+
* Check if text contains a @mention for this agent.
|
|
12525
|
+
*/
|
|
12526
|
+
matchesMention(text, config) {
|
|
12527
|
+
const lower = text.toLowerCase();
|
|
12528
|
+
const names = [];
|
|
12529
|
+
if (config?.agentDisplayName) names.push(config.agentDisplayName);
|
|
12530
|
+
if (this.agentDisplayName) names.push(this.agentDisplayName);
|
|
12531
|
+
if (this.agentName) names.push(this.agentName);
|
|
12532
|
+
for (const name of names) {
|
|
12533
|
+
if (lower.includes(`@${name.toLowerCase()}`)) return true;
|
|
12534
|
+
}
|
|
12535
|
+
return false;
|
|
12536
|
+
}
|
|
12494
12537
|
/**
|
|
12495
12538
|
* Gate a message through bot-enabled filtering + owner suppression.
|
|
12496
12539
|
* Returns true if the message should be processed, false to skip.
|
|
@@ -12500,11 +12543,6 @@ var RealtimeGateway = class {
|
|
|
12500
12543
|
let config = this.botConfigCache.get(groupId);
|
|
12501
12544
|
const groupType = eventData?.groupType ?? config?.groupType;
|
|
12502
12545
|
if (groupType === "agent_channel") return true;
|
|
12503
|
-
const securityMode = eventData?.groupSecurityMode ?? "PLAINTEXT_SDK";
|
|
12504
|
-
if (securityMode !== "PLAINTEXT_SDK" && !this.e2eeGrantedGroups.has(groupId)) {
|
|
12505
|
-
this.log.debug?.(`Skipping message in ${groupId}: security mode ${securityMode} and no E2EE grant`);
|
|
12506
|
-
return false;
|
|
12507
|
-
}
|
|
12508
12546
|
if (!config) {
|
|
12509
12547
|
if (this.onCacheMiss) {
|
|
12510
12548
|
try {
|
|
@@ -12528,6 +12566,41 @@ var RealtimeGateway = class {
|
|
|
12528
12566
|
this.log.debug?.(`Skipping message in ${groupId}: bot disabled`);
|
|
12529
12567
|
return false;
|
|
12530
12568
|
}
|
|
12569
|
+
if (config.source === "directory" && config.triggerMode) {
|
|
12570
|
+
if (config.triggerMode === "all_messages") {
|
|
12571
|
+
const secMode = config.securityMode ?? eventData?.groupSecurityMode ?? "PLAINTEXT_SDK";
|
|
12572
|
+
if (secMode !== "PLAINTEXT_SDK" && !this.e2eeGrantedGroups.has(groupId)) {
|
|
12573
|
+
this.log.debug?.(`Skipping directory all_messages in ${groupId}: E2EE but no grant`);
|
|
12574
|
+
return false;
|
|
12575
|
+
}
|
|
12576
|
+
return true;
|
|
12577
|
+
}
|
|
12578
|
+
const plaintext = eventData?.plaintext;
|
|
12579
|
+
if (plaintext) {
|
|
12580
|
+
return this.matchesMention(plaintext, config);
|
|
12581
|
+
}
|
|
12582
|
+
if (eventData?.ciphertext && this.keyManager && this.e2eeGrantedGroups.has(groupId)) {
|
|
12583
|
+
try {
|
|
12584
|
+
const epoch = parseGroupEnvelopeEpoch(eventData.ciphertext);
|
|
12585
|
+
if (epoch !== null) {
|
|
12586
|
+
const cached = await this.keyManager.getEpochKey(groupId, epoch);
|
|
12587
|
+
if (cached) {
|
|
12588
|
+
const decrypted = decryptGroupMessage(eventData.ciphertext, cached.key);
|
|
12589
|
+
return this.matchesMention(decrypted, config);
|
|
12590
|
+
}
|
|
12591
|
+
}
|
|
12592
|
+
} catch (err) {
|
|
12593
|
+
this.log.warn?.(`Mention check decrypt failed for group ${groupId}: ${err}`);
|
|
12594
|
+
}
|
|
12595
|
+
}
|
|
12596
|
+
this.log.debug?.(`Skipping mention-mode message in ${groupId}: no readable text`);
|
|
12597
|
+
return false;
|
|
12598
|
+
}
|
|
12599
|
+
const securityMode = config.securityMode ?? eventData?.groupSecurityMode ?? "PLAINTEXT_SDK";
|
|
12600
|
+
if (securityMode !== "PLAINTEXT_SDK" && !this.e2eeGrantedGroups.has(groupId)) {
|
|
12601
|
+
this.log.debug?.(`Skipping message in ${groupId}: security mode ${securityMode} and no E2EE grant`);
|
|
12602
|
+
return false;
|
|
12603
|
+
}
|
|
12531
12604
|
const ownerIdentityId = eventData?.groupOwnerIdentityId ?? config.ownerIdentityId;
|
|
12532
12605
|
if (ownerIdentityId && senderId === ownerIdentityId) {
|
|
12533
12606
|
const suppressMinutes = config.suppressionMinutes ?? 10;
|
|
@@ -12674,13 +12747,33 @@ var RealtimeGateway = class {
|
|
|
12674
12747
|
this.keyManager.invalidate(data.groupId);
|
|
12675
12748
|
}
|
|
12676
12749
|
});
|
|
12750
|
+
channel.bind("agent:group-added", (data) => {
|
|
12751
|
+
log.info?.(`agent:group-added for group ${data.groupId} (${data.groupName}) triggerMode=${data.triggerMode}`);
|
|
12752
|
+
this.botConfigCache.set(data.groupId, {
|
|
12753
|
+
enabled: true,
|
|
12754
|
+
suppressionMinutes: 10,
|
|
12755
|
+
ownerIdentityId: "",
|
|
12756
|
+
groupType: data.groupType,
|
|
12757
|
+
grantedScopes: data.grantedScopes,
|
|
12758
|
+
triggerMode: data.triggerMode,
|
|
12759
|
+
source: "directory",
|
|
12760
|
+
agentDisplayName: this.agentDisplayName ?? void 0,
|
|
12761
|
+
securityMode: data.securityMode
|
|
12762
|
+
});
|
|
12763
|
+
});
|
|
12764
|
+
channel.bind("agent:group-removed", (data) => {
|
|
12765
|
+
log.info?.(`agent:group-removed for group ${data.groupId}`);
|
|
12766
|
+
this.botConfigCache.delete(data.groupId);
|
|
12767
|
+
});
|
|
12677
12768
|
channel.bind("new-group-message", (data) => {
|
|
12678
12769
|
log.info?.(`Pusher event: messageId=${data.messageId} groupId=${data.groupId} sender=${data.senderUsername} plaintext=${!!data.plaintext}`);
|
|
12679
12770
|
if (data.senderId === this.botIdentityId) return;
|
|
12680
12771
|
this.shouldProcessMessage(data.groupId, data.senderId, {
|
|
12681
12772
|
groupSecurityMode: data.groupSecurityMode,
|
|
12682
12773
|
groupOwnerIdentityId: data.groupOwnerIdentityId,
|
|
12683
|
-
groupType: data.groupType
|
|
12774
|
+
groupType: data.groupType,
|
|
12775
|
+
plaintext: data.plaintext,
|
|
12776
|
+
ciphertext: data.ciphertext
|
|
12684
12777
|
}).then((shouldProcess) => {
|
|
12685
12778
|
if (!shouldProcess) return;
|
|
12686
12779
|
if (data.plaintext) {
|
|
@@ -13454,6 +13547,8 @@ var quiuboPlugin = {
|
|
|
13454
13547
|
error: (...args) => log.error?.(`[${accountId}] Quiubo:`, ...args)
|
|
13455
13548
|
} : void 0;
|
|
13456
13549
|
let resolvedAgentId;
|
|
13550
|
+
let resolvedAgentDisplayName;
|
|
13551
|
+
let resolvedAgentName;
|
|
13457
13552
|
let agentKeys;
|
|
13458
13553
|
try {
|
|
13459
13554
|
const { agents: agentList } = await client.listAgents();
|
|
@@ -13468,7 +13563,10 @@ var quiuboPlugin = {
|
|
|
13468
13563
|
try {
|
|
13469
13564
|
const created = await client.createAgent({
|
|
13470
13565
|
identityId: botIdentityId,
|
|
13471
|
-
name: `bot-${accountId}
|
|
13566
|
+
name: `bot-${accountId}`,
|
|
13567
|
+
directoryEnabled: true,
|
|
13568
|
+
offeredScopes: ["send_messages", "read_messages"],
|
|
13569
|
+
directoryDisplayName: auth?.appName ?? accountId
|
|
13472
13570
|
});
|
|
13473
13571
|
match = { ...created, scopes: created.scopes ?? ["send_messages", "read_messages"] };
|
|
13474
13572
|
log?.info?.(`[${accountId}] Quiubo: auto-registered agent ${created.id}`);
|
|
@@ -13479,7 +13577,9 @@ var quiuboPlugin = {
|
|
|
13479
13577
|
}
|
|
13480
13578
|
if (match) {
|
|
13481
13579
|
resolvedAgentId = match.id;
|
|
13482
|
-
|
|
13580
|
+
resolvedAgentDisplayName = match.directoryDisplayName ?? match.name ?? auth.appName;
|
|
13581
|
+
resolvedAgentName = match.name;
|
|
13582
|
+
log?.info?.(`[${accountId}] Quiubo: resolved agent ${match.name} (${match.id}) for heartbeat, displayName=${resolvedAgentDisplayName}`);
|
|
13483
13583
|
try {
|
|
13484
13584
|
const keys = loadOrGenerateKeys(quiuboConfig);
|
|
13485
13585
|
if (!match.e2eeConfigured) {
|
|
@@ -13522,6 +13622,8 @@ var quiuboPlugin = {
|
|
|
13522
13622
|
agentId: resolvedAgentId,
|
|
13523
13623
|
keyManager,
|
|
13524
13624
|
e2eeGrantedGroups,
|
|
13625
|
+
agentName: resolvedAgentName,
|
|
13626
|
+
agentDisplayName: resolvedAgentDisplayName,
|
|
13525
13627
|
onCacheMiss: async (groupId) => {
|
|
13526
13628
|
try {
|
|
13527
13629
|
const [group, { members }] = await Promise.all([
|
|
@@ -13536,8 +13638,26 @@ var quiuboPlugin = {
|
|
|
13536
13638
|
ownerIdentityId: owner?.identityId ?? "",
|
|
13537
13639
|
groupType: group.groupType ?? "standard"
|
|
13538
13640
|
};
|
|
13539
|
-
} catch
|
|
13540
|
-
|
|
13641
|
+
} catch {
|
|
13642
|
+
if (resolvedAgentId) {
|
|
13643
|
+
try {
|
|
13644
|
+
const status = await client.getGroupAgent(resolvedAgentId, groupId);
|
|
13645
|
+
if (status) {
|
|
13646
|
+
return {
|
|
13647
|
+
enabled: status.enabled,
|
|
13648
|
+
suppressionMinutes: 10,
|
|
13649
|
+
ownerIdentityId: "",
|
|
13650
|
+
groupType: "standard",
|
|
13651
|
+
triggerMode: status.triggerMode,
|
|
13652
|
+
source: "directory",
|
|
13653
|
+
grantedScopes: status.grantedScopes,
|
|
13654
|
+
agentDisplayName: resolvedAgentDisplayName
|
|
13655
|
+
};
|
|
13656
|
+
}
|
|
13657
|
+
} catch (dirErr) {
|
|
13658
|
+
log?.warn?.(`[${accountId}] Quiubo: directory agent status fetch failed for group ${groupId}: ${dirErr}`);
|
|
13659
|
+
}
|
|
13660
|
+
}
|
|
13541
13661
|
return null;
|
|
13542
13662
|
}
|
|
13543
13663
|
},
|
|
@@ -13587,37 +13707,73 @@ var quiuboPlugin = {
|
|
|
13587
13707
|
gateways.set(accountId, gateway);
|
|
13588
13708
|
await gateway.start();
|
|
13589
13709
|
try {
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
const
|
|
13593
|
-
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13710
|
+
if (resolvedAgentId) {
|
|
13711
|
+
const { groups: agentGroups } = await client.listAgentGroups(resolvedAgentId);
|
|
13712
|
+
for (const ag of agentGroups) {
|
|
13713
|
+
if (ag.source === "directory") {
|
|
13714
|
+
gateway.setBotConfig(ag.groupId, {
|
|
13715
|
+
enabled: ag.enabled ?? true,
|
|
13716
|
+
suppressionMinutes: 10,
|
|
13717
|
+
ownerIdentityId: "",
|
|
13718
|
+
groupType: ag.groupType ?? "standard",
|
|
13719
|
+
triggerMode: ag.triggerMode,
|
|
13720
|
+
source: "directory",
|
|
13721
|
+
grantedScopes: ag.grantedScopes,
|
|
13722
|
+
agentDisplayName: ag.agentDisplayName ?? resolvedAgentDisplayName,
|
|
13723
|
+
securityMode: ag.securityMode
|
|
13724
|
+
});
|
|
13725
|
+
} else {
|
|
13726
|
+
const botSettings = ag.settings?.bot ?? {};
|
|
13727
|
+
let ownerIdentityId = "";
|
|
13728
|
+
try {
|
|
13729
|
+
const { members } = await client.listGroupMembers(ag.groupId);
|
|
13730
|
+
const owner = members.find((m) => m.role === "owner");
|
|
13731
|
+
ownerIdentityId = owner?.identityId ?? "";
|
|
13732
|
+
} catch {
|
|
13733
|
+
}
|
|
13734
|
+
gateway.setBotConfig(ag.groupId, {
|
|
13735
|
+
enabled: botSettings.enabled ?? false,
|
|
13736
|
+
suppressionMinutes: botSettings.suppressionMinutes ?? 10,
|
|
13737
|
+
ownerIdentityId,
|
|
13738
|
+
groupType: ag.groupType ?? "standard",
|
|
13739
|
+
securityMode: ag.securityMode
|
|
13740
|
+
});
|
|
13741
|
+
}
|
|
13599
13742
|
}
|
|
13600
|
-
|
|
13601
|
-
|
|
13602
|
-
|
|
13603
|
-
|
|
13604
|
-
|
|
13605
|
-
|
|
13606
|
-
|
|
13607
|
-
|
|
13608
|
-
|
|
13743
|
+
log?.info?.(`[${accountId}] Quiubo: hydrated bot config for ${agentGroups.length} group(s) via listAgentGroups`);
|
|
13744
|
+
if (keyManager) {
|
|
13745
|
+
for (const ag of agentGroups) {
|
|
13746
|
+
try {
|
|
13747
|
+
const cached = await keyManager.getActiveEpochKey(ag.groupId);
|
|
13748
|
+
if (cached) {
|
|
13749
|
+
e2eeGrantedGroups.add(ag.groupId);
|
|
13750
|
+
}
|
|
13751
|
+
} catch {
|
|
13752
|
+
}
|
|
13753
|
+
}
|
|
13754
|
+
if (e2eeGrantedGroups.size > 0) {
|
|
13755
|
+
log?.info?.(`[${accountId}] Quiubo: E2EE grants hydrated for ${e2eeGrantedGroups.size} group(s)`);
|
|
13756
|
+
}
|
|
13757
|
+
}
|
|
13758
|
+
} else {
|
|
13759
|
+
const { groups: allGroups } = await client.listGroups(botIdentityId);
|
|
13609
13760
|
for (const group of allGroups) {
|
|
13761
|
+
const botSettings = group.settings?.bot ?? {};
|
|
13762
|
+
let ownerIdentityId = "";
|
|
13610
13763
|
try {
|
|
13611
|
-
const
|
|
13612
|
-
|
|
13613
|
-
|
|
13614
|
-
}
|
|
13764
|
+
const { members } = await client.listGroupMembers(group.id);
|
|
13765
|
+
const owner = members.find((m) => m.role === "owner");
|
|
13766
|
+
ownerIdentityId = owner?.identityId ?? "";
|
|
13615
13767
|
} catch {
|
|
13616
13768
|
}
|
|
13769
|
+
gateway.setBotConfig(group.id, {
|
|
13770
|
+
enabled: botSettings.enabled ?? false,
|
|
13771
|
+
suppressionMinutes: botSettings.suppressionMinutes ?? 10,
|
|
13772
|
+
ownerIdentityId,
|
|
13773
|
+
groupType: group.groupType ?? "standard"
|
|
13774
|
+
});
|
|
13617
13775
|
}
|
|
13618
|
-
|
|
13619
|
-
log?.info?.(`[${accountId}] Quiubo: E2EE grants hydrated for ${e2eeGrantedGroups.size} group(s)`);
|
|
13620
|
-
}
|
|
13776
|
+
log?.info?.(`[${accountId}] Quiubo: hydrated bot config for ${allGroups.length} group(s) via listGroups`);
|
|
13621
13777
|
}
|
|
13622
13778
|
} catch (err) {
|
|
13623
13779
|
log?.warn?.(`[${accountId}] Quiubo: failed to hydrate bot config (non-fatal): ${err}`);
|
|
@@ -13770,6 +13926,12 @@ async function routeInboundMessage(opts) {
|
|
|
13770
13926
|
log?.debug?.(`[${accountId}] Quiubo: skipping empty ${info.kind} reply`);
|
|
13771
13927
|
return;
|
|
13772
13928
|
}
|
|
13929
|
+
const gw = gateways.get(accountId);
|
|
13930
|
+
const cachedConfig = gw?.getBotConfig(groupId);
|
|
13931
|
+
if (cachedConfig?.grantedScopes && !cachedConfig.grantedScopes.includes("send_messages")) {
|
|
13932
|
+
log?.warn?.(`[${accountId}] Quiubo: skipping outbound \u2014 agent lacks send_messages scope for group ${groupId}`);
|
|
13933
|
+
return;
|
|
13934
|
+
}
|
|
13773
13935
|
log?.info?.(`[${accountId}] Quiubo: delivering ${info.kind} reply [${payload.text?.length ?? 0} chars, ${mdAttachments.length} attachments]`);
|
|
13774
13936
|
try {
|
|
13775
13937
|
let ciphertextOut;
|