openclaw-quiubo 2.6.15 → 2.6.16

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
@@ -12451,7 +12451,6 @@ function signChallenge(challengeBase64, signingPrivateKey) {
12451
12451
 
12452
12452
  // src/realtime-gateway.ts
12453
12453
  var CURSORS_DIR = join(process.env.HOME ?? process.env.USERPROFILE ?? "", ".openclaw", "cron");
12454
- var CURSORS_FILE = join(CURSORS_DIR, "quiubo-cursors.json");
12455
12454
  var consoleLogger = {
12456
12455
  debug: (...args) => console.debug("[Quiubo]", ...args),
12457
12456
  info: (...args) => console.log("[Quiubo]", ...args),
@@ -12496,6 +12495,7 @@ var RealtimeGateway = class {
12496
12495
  heartbeatTimer = null;
12497
12496
  // Cursor save debounce (avoids disk thrash on rapid Pusher events)
12498
12497
  cursorSaveTimer = null;
12498
+ cursorsFile;
12499
12499
  // Pusher connection timeout (cancelled on stop to prevent stale firing)
12500
12500
  pusherConnectTimeout = null;
12501
12501
  // E2EE state
@@ -12508,6 +12508,7 @@ var RealtimeGateway = class {
12508
12508
  constructor(opts) {
12509
12509
  this.client = opts.client;
12510
12510
  this.botIdentityId = opts.botIdentityId;
12511
+ this.cursorsFile = join(CURSORS_DIR, `quiubo-cursors-${opts.accountId}.json`);
12511
12512
  this.pusherConfig = opts.pusher ?? null;
12512
12513
  this.pollIntervalMs = opts.pollIntervalMs ?? 5e3;
12513
12514
  this.onMessage = opts.onMessage;
@@ -12526,7 +12527,7 @@ var RealtimeGateway = class {
12526
12527
  */
12527
12528
  async loadCursors() {
12528
12529
  try {
12529
- const raw = await readFile(CURSORS_FILE, "utf-8");
12530
+ const raw = await readFile(this.cursorsFile, "utf-8");
12530
12531
  const data = JSON.parse(raw);
12531
12532
  let count = 0;
12532
12533
  for (const [groupId, cursor] of Object.entries(data)) {
@@ -12551,7 +12552,7 @@ var RealtimeGateway = class {
12551
12552
  data[groupId] = cursor;
12552
12553
  }
12553
12554
  await mkdir(CURSORS_DIR, { recursive: true });
12554
- await writeFile(CURSORS_FILE, JSON.stringify(data, null, 2), "utf-8");
12555
+ await writeFile(this.cursorsFile, JSON.stringify(data, null, 2), "utf-8");
12555
12556
  } catch (err) {
12556
12557
  this.log.warn?.(`Failed to persist cursors: ${err}`);
12557
12558
  }
@@ -12932,6 +12933,9 @@ var RealtimeGateway = class {
12932
12933
  */
12933
12934
  async fetchAndRouteMessage(groupId, messageId) {
12934
12935
  try {
12936
+ if (!this.cursors.has(groupId)) {
12937
+ await this.seekToLatest(groupId);
12938
+ }
12935
12939
  const cursor = this.cursors.get(groupId);
12936
12940
  const { messages } = await this.client.listMessages(groupId, cursor, 100);
12937
12941
  if (messages.length > 0) {
@@ -12940,10 +12944,6 @@ var RealtimeGateway = class {
12940
12944
  }
12941
12945
  const msg = messages.find((m) => m.id === messageId);
12942
12946
  if (!msg) {
12943
- if (!cursor) {
12944
- this.log.info?.(`fetchAndRoute: no cursor for group ${groupId}, caught up to latest (skip history)`);
12945
- return;
12946
- }
12947
12947
  this.log.warn?.(`Message ${messageId} not found in group ${groupId} (fetched ${messages.length} from cursor)`);
12948
12948
  return;
12949
12949
  }
@@ -13090,14 +13090,36 @@ var RealtimeGateway = class {
13090
13090
  this.polling = false;
13091
13091
  }
13092
13092
  }
13093
+ /**
13094
+ * Seek cursor to the latest message without processing anything.
13095
+ * Called on cold start (no cursor for a group) to avoid replaying history.
13096
+ * Paginates forward until there are no more messages.
13097
+ */
13098
+ async seekToLatest(groupId) {
13099
+ let cursor;
13100
+ let hasMore = true;
13101
+ while (hasMore) {
13102
+ const result = await this.client.listMessages(groupId, cursor, 100);
13103
+ if (result.messages.length === 0) break;
13104
+ cursor = result.messages[result.messages.length - 1].id;
13105
+ hasMore = result.hasMore;
13106
+ }
13107
+ if (cursor) {
13108
+ this.cursors.set(groupId, cursor);
13109
+ this.log.info?.(`Seeked to latest in group ${groupId}: cursor=${cursor}`);
13110
+ }
13111
+ }
13093
13112
  async pollGroup(groupId) {
13094
13113
  try {
13095
13114
  const cursor = this.cursors.get(groupId);
13115
+ if (!cursor) {
13116
+ await this.seekToLatest(groupId);
13117
+ return;
13118
+ }
13096
13119
  const { messages } = await this.client.listMessages(groupId, cursor);
13097
13120
  if (messages.length === 0) return;
13098
13121
  const lastMessage = messages[messages.length - 1];
13099
13122
  this.cursors.set(groupId, lastMessage.id);
13100
- if (!cursor) return;
13101
13123
  for (const msg of messages) {
13102
13124
  if (msg.senderIdentityId === this.botIdentityId) continue;
13103
13125
  let plaintext = msg.plaintext;
@@ -13807,6 +13829,7 @@ var quiuboPlugin = {
13807
13829
  const gateway = new RealtimeGateway({
13808
13830
  client,
13809
13831
  botIdentityId,
13832
+ accountId,
13810
13833
  pusher: pusherConfig,
13811
13834
  pollIntervalMs: pollIntervalMs ?? 5e3,
13812
13835
  log: gatewayLog,