@openclaw/nextcloud-talk 2026.6.5-beta.2 → 2026.6.5-beta.5

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/api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as nextcloudTalkPlugin } from "./channel-5flc23d2.js";
1
+ import { t as nextcloudTalkPlugin } from "./channel-Cg-iYKsC.js";
2
2
  export { nextcloudTalkPlugin };
@@ -28,14 +28,16 @@ import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
28
28
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
29
29
  import { requireChannelOpenAllowFrom, resolveLoggerBackedRuntime, runStoppablePassiveMonitor, safeParseJsonWithSchema } from "openclaw/plugin-sdk/extension-shared";
30
30
  import { z } from "zod";
31
+ import fs from "node:fs/promises";
31
32
  import os from "node:os";
33
+ import path from "node:path";
34
+ import { createClaimableDedupe, migratePersistentDedupeLegacyJsonFile } from "openclaw/plugin-sdk/persistent-dedupe";
35
+ import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
32
36
  import { channelIngressRoutes, resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
33
37
  import { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";
34
38
  import { buildChannelKeyCandidates, normalizeChannelSlug, resolveChannelEntryMatchWithFallback, resolveNestedAllowlistDecision } from "openclaw/plugin-sdk/channel-targets";
35
39
  import { createServer } from "node:http";
36
40
  import { WEBHOOK_RATE_LIMIT_DEFAULTS, createAuthRateLimiter, isRequestBodyLimitError, readRequestBodyWithLimit, requestBodyErrorToText } from "openclaw/plugin-sdk/webhook-ingress";
37
- import path from "node:path";
38
- import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
39
41
  import { jsonResult, readStringParam, resolveReactionMessageId } from "openclaw/plugin-sdk/channel-actions";
40
42
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, buildOutboundBaseSessionKey, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/routing";
41
43
  import { applyAccountNameToChannelSection, createSetupTranslator, createStandardChannelSetupStatus, formatDocsLink, patchScopedAccountConfig, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
@@ -424,7 +426,91 @@ const NextcloudTalkConfigSchema = NextcloudTalkAccountSchemaBase.extend({
424
426
  });
425
427
  });
426
428
  //#endregion
429
+ //#region extensions/nextcloud-talk/src/replay-guard.ts
430
+ const NEXTCLOUD_TALK_PLUGIN_ID = "nextcloud-talk";
431
+ const NEXTCLOUD_TALK_REPLAY_DEDUPE_NAMESPACE_PREFIX = "replay-dedupe";
432
+ const DEFAULT_REPLAY_TTL_MS = 1440 * 60 * 1e3;
433
+ const DEFAULT_MEMORY_MAX_SIZE = 1e3;
434
+ const DEFAULT_STATE_MAX_ENTRIES = 1e4;
435
+ function buildReplayKey(params) {
436
+ const roomToken = params.roomToken.trim();
437
+ const messageId = params.messageId.trim();
438
+ if (!roomToken || !messageId) return null;
439
+ return `${roomToken}:${messageId}`;
440
+ }
441
+ function createNextcloudTalkReplayGuard(options) {
442
+ const stateDir = options.stateDir?.trim();
443
+ const baseOptions = {
444
+ ttlMs: options.ttlMs ?? DEFAULT_REPLAY_TTL_MS,
445
+ memoryMaxSize: options.memoryMaxSize ?? DEFAULT_MEMORY_MAX_SIZE
446
+ };
447
+ const dedupe = createClaimableDedupe(stateDir ? {
448
+ ...baseOptions,
449
+ pluginId: NEXTCLOUD_TALK_PLUGIN_ID,
450
+ namespacePrefix: NEXTCLOUD_TALK_REPLAY_DEDUPE_NAMESPACE_PREFIX,
451
+ stateMaxEntries: options.stateMaxEntries ?? options.fileMaxEntries ?? DEFAULT_STATE_MAX_ENTRIES,
452
+ env: {
453
+ ...process.env,
454
+ OPENCLAW_STATE_DIR: stateDir
455
+ },
456
+ onDiskError: options.onDiskError
457
+ } : baseOptions);
458
+ return {
459
+ claimMessage: async ({ accountId, roomToken, messageId }) => {
460
+ const replayKey = buildReplayKey({
461
+ roomToken,
462
+ messageId
463
+ });
464
+ if (!replayKey) return "invalid";
465
+ return (await dedupe.claim(replayKey, { namespace: accountId })).kind;
466
+ },
467
+ commitMessage: async ({ accountId, roomToken, messageId }) => {
468
+ const replayKey = buildReplayKey({
469
+ roomToken,
470
+ messageId
471
+ });
472
+ if (!replayKey) return true;
473
+ return await dedupe.commit(replayKey, { namespace: accountId });
474
+ },
475
+ releaseMessage: ({ accountId, roomToken, messageId, error }) => {
476
+ const replayKey = buildReplayKey({
477
+ roomToken,
478
+ messageId
479
+ });
480
+ if (!replayKey) return;
481
+ dedupe.release(replayKey, {
482
+ namespace: accountId,
483
+ error
484
+ });
485
+ },
486
+ shouldProcessMessage: async ({ accountId, roomToken, messageId }) => {
487
+ const replayKey = buildReplayKey({
488
+ roomToken,
489
+ messageId
490
+ });
491
+ if (!replayKey) return true;
492
+ if ((await dedupe.claim(replayKey, { namespace: accountId })).kind !== "claimed") return false;
493
+ return await dedupe.commit(replayKey, { namespace: accountId });
494
+ }
495
+ };
496
+ }
497
+ //#endregion
427
498
  //#region extensions/nextcloud-talk/src/doctor.ts
499
+ const REPLAY_DEDUPE_TTL_MS = 1440 * 60 * 1e3;
500
+ const REPLAY_DEDUPE_MAX_ENTRIES = 1e4;
501
+ function sanitizeLegacyReplaySegment(value) {
502
+ const trimmed = value.trim();
503
+ if (!trimmed) return "default";
504
+ return trimmed.replace(/[^a-zA-Z0-9_-]/g, "_");
505
+ }
506
+ async function fileExists(filePath) {
507
+ try {
508
+ await fs.access(filePath);
509
+ return true;
510
+ } catch {
511
+ return false;
512
+ }
513
+ }
428
514
  async function collectNextcloudTalkBotResponseWarnings(params) {
429
515
  const warnings = [];
430
516
  for (const accountId of listNextcloudTalkAccountIds(params.cfg)) {
@@ -441,10 +527,51 @@ async function collectNextcloudTalkBotResponseWarnings(params) {
441
527
  }
442
528
  return warnings;
443
529
  }
530
+ async function repairNextcloudTalkReplayDedupeState(params) {
531
+ const changes = [];
532
+ const warnings = [];
533
+ const env = params.env ?? process.env;
534
+ const stateDir = resolveStateDir(env, os.homedir);
535
+ const replayDir = path.join(stateDir, "nextcloud-talk", "replay-dedupe");
536
+ for (const accountId of listNextcloudTalkAccountIds(params.cfg)) {
537
+ const legacyPath = path.join(replayDir, `${sanitizeLegacyReplaySegment(accountId)}.json`);
538
+ if (!await fileExists(legacyPath)) continue;
539
+ try {
540
+ const result = await migratePersistentDedupeLegacyJsonFile({
541
+ filePath: legacyPath,
542
+ namespace: accountId,
543
+ ttlMs: REPLAY_DEDUPE_TTL_MS,
544
+ memoryMaxSize: 0,
545
+ pluginId: NEXTCLOUD_TALK_PLUGIN_ID,
546
+ namespacePrefix: NEXTCLOUD_TALK_REPLAY_DEDUPE_NAMESPACE_PREFIX,
547
+ stateMaxEntries: REPLAY_DEDUPE_MAX_ENTRIES,
548
+ env
549
+ });
550
+ changes.push(`Migrated Nextcloud Talk replay dedupe cache for account "${accountId}" to SQLite (${result.imported} imported, ${result.skippedExpired} expired, ${result.skippedExisting} already current).`);
551
+ } catch (error) {
552
+ warnings.push(`Skipped Nextcloud Talk replay dedupe cache for account "${accountId}": ${String(error)}`);
553
+ }
554
+ }
555
+ return {
556
+ changes,
557
+ warnings
558
+ };
559
+ }
444
560
  const nextcloudTalkDoctor = {
445
561
  legacyConfigRules,
446
562
  normalizeCompatibilityConfig,
447
- collectPreviewWarnings: async ({ cfg }) => await collectNextcloudTalkBotResponseWarnings({ cfg })
563
+ collectPreviewWarnings: async ({ cfg }) => await collectNextcloudTalkBotResponseWarnings({ cfg }),
564
+ repairConfig: async ({ cfg, env }) => {
565
+ const repair = await repairNextcloudTalkReplayDedupeState({
566
+ cfg,
567
+ ...env ? { env } : {}
568
+ });
569
+ return {
570
+ config: cfg,
571
+ changes: repair.changes,
572
+ warnings: repair.warnings
573
+ };
574
+ }
448
575
  };
449
576
  //#endregion
450
577
  //#region extensions/nextcloud-talk/src/policy.ts
@@ -1318,73 +1445,6 @@ function createNextcloudTalkWebhookServer(opts) {
1318
1445
  };
1319
1446
  }
1320
1447
  //#endregion
1321
- //#region extensions/nextcloud-talk/src/replay-guard.ts
1322
- const DEFAULT_REPLAY_TTL_MS = 1440 * 60 * 1e3;
1323
- const DEFAULT_MEMORY_MAX_SIZE = 1e3;
1324
- const DEFAULT_FILE_MAX_ENTRIES = 1e4;
1325
- function sanitizeSegment(value) {
1326
- const trimmed = value.trim();
1327
- if (!trimmed) return "default";
1328
- return trimmed.replace(/[^a-zA-Z0-9_-]/g, "_");
1329
- }
1330
- function buildReplayKey(params) {
1331
- const roomToken = params.roomToken.trim();
1332
- const messageId = params.messageId.trim();
1333
- if (!roomToken || !messageId) return null;
1334
- return `${roomToken}:${messageId}`;
1335
- }
1336
- function createNextcloudTalkReplayGuard(options) {
1337
- const stateDir = options.stateDir?.trim();
1338
- const baseOptions = {
1339
- ttlMs: options.ttlMs ?? DEFAULT_REPLAY_TTL_MS,
1340
- memoryMaxSize: options.memoryMaxSize ?? DEFAULT_MEMORY_MAX_SIZE
1341
- };
1342
- const dedupe = createClaimableDedupe(stateDir ? {
1343
- ...baseOptions,
1344
- fileMaxEntries: options.fileMaxEntries ?? DEFAULT_FILE_MAX_ENTRIES,
1345
- resolveFilePath: (namespace) => path.join(stateDir, "nextcloud-talk", "replay-dedupe", `${sanitizeSegment(namespace)}.json`),
1346
- onDiskError: options.onDiskError
1347
- } : baseOptions);
1348
- return {
1349
- claimMessage: async ({ accountId, roomToken, messageId }) => {
1350
- const replayKey = buildReplayKey({
1351
- roomToken,
1352
- messageId
1353
- });
1354
- if (!replayKey) return "invalid";
1355
- return (await dedupe.claim(replayKey, { namespace: accountId })).kind;
1356
- },
1357
- commitMessage: async ({ accountId, roomToken, messageId }) => {
1358
- const replayKey = buildReplayKey({
1359
- roomToken,
1360
- messageId
1361
- });
1362
- if (!replayKey) return true;
1363
- return await dedupe.commit(replayKey, { namespace: accountId });
1364
- },
1365
- releaseMessage: ({ accountId, roomToken, messageId, error }) => {
1366
- const replayKey = buildReplayKey({
1367
- roomToken,
1368
- messageId
1369
- });
1370
- if (!replayKey) return;
1371
- dedupe.release(replayKey, {
1372
- namespace: accountId,
1373
- error
1374
- });
1375
- },
1376
- shouldProcessMessage: async ({ accountId, roomToken, messageId }) => {
1377
- const replayKey = buildReplayKey({
1378
- roomToken,
1379
- messageId
1380
- });
1381
- if (!replayKey) return true;
1382
- if ((await dedupe.claim(replayKey, { namespace: accountId })).kind !== "claimed") return false;
1383
- return await dedupe.commit(replayKey, { namespace: accountId });
1384
- }
1385
- };
1386
- }
1387
- //#endregion
1388
1448
  //#region extensions/nextcloud-talk/src/monitor-runtime.ts
1389
1449
  const DEFAULT_WEBHOOK_PORT = 8788;
1390
1450
  const DEFAULT_WEBHOOK_HOST = "0.0.0.0";
@@ -1,2 +1,2 @@
1
- import { t as nextcloudTalkPlugin } from "./channel-5flc23d2.js";
1
+ import { t as nextcloudTalkPlugin } from "./channel-Cg-iYKsC.js";
2
2
  export { nextcloudTalkPlugin };
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@openclaw/nextcloud-talk",
3
- "version": "2026.6.5-beta.2",
3
+ "version": "2026.6.5-beta.5",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/nextcloud-talk",
9
- "version": "2026.6.5-beta.2",
9
+ "version": "2026.6.5-beta.5",
10
10
  "dependencies": {
11
11
  "zod": "4.4.3"
12
12
  },
13
13
  "peerDependencies": {
14
- "openclaw": ">=2026.6.5-beta.2"
14
+ "openclaw": ">=2026.6.5-beta.5"
15
15
  },
16
16
  "peerDependenciesMeta": {
17
17
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/nextcloud-talk",
3
- "version": "2026.6.5-beta.2",
3
+ "version": "2026.6.5-beta.5",
4
4
  "description": "OpenClaw Nextcloud Talk channel plugin for conversations.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "type": "module",
10
10
  "peerDependencies": {
11
- "openclaw": ">=2026.6.5-beta.2"
11
+ "openclaw": ">=2026.6.5-beta.5"
12
12
  },
13
13
  "peerDependenciesMeta": {
14
14
  "openclaw": {
@@ -40,10 +40,10 @@
40
40
  "minHostVersion": ">=2026.4.10"
41
41
  },
42
42
  "compat": {
43
- "pluginApi": ">=2026.6.5-beta.2"
43
+ "pluginApi": ">=2026.6.5-beta.5"
44
44
  },
45
45
  "build": {
46
- "openclawVersion": "2026.6.5-beta.2"
46
+ "openclawVersion": "2026.6.5-beta.5"
47
47
  },
48
48
  "release": {
49
49
  "publishToClawHub": true,