@openclaw/nextcloud-talk 2026.8.1-beta.2 → 2026.9.1-beta.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/dist/api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as nextcloudTalkPlugin } from "./channel-CojgyJSq.js";
1
+ import { t as nextcloudTalkPlugin } from "./channel-tgvjpexV.js";
2
2
  export { nextcloudTalkPlugin };
@@ -1,2 +1,2 @@
1
- import { t as nextcloudTalkPlugin } from "./channel-CojgyJSq.js";
1
+ import { t as nextcloudTalkPlugin } from "./channel-tgvjpexV.js";
2
2
  export { nextcloudTalkPlugin };
@@ -4,7 +4,7 @@ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries
4
4
  import { createAccountListHelpers, describeWebhookAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
5
5
  import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
6
6
  import { createLoggedPairingApprovalNotifier, createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
7
- import { createAllowlistProviderRouteAllowlistWarningCollector, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
7
+ import { createAllowlistProviderRouteAllowlistWarningCollector, createConditionalWarningCollector, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
8
8
  import { buildWebhookChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
9
9
  import { FormatCapabilityProfile, convertMarkdownTables, renderMarkdownWithMarkers, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
10
10
  import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
@@ -12,7 +12,7 @@ import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, norm
12
12
  import { DEFAULT_ACCOUNT_ID, hasConfiguredAccountValue, normalizeAccountId, resolveAccountWithDefaultFallback } from "openclaw/plugin-sdk/account-core";
13
13
  import { isTruthyEnvValue } from "openclaw/plugin-sdk/runtime-env";
14
14
  import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
15
- import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
15
+ import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
16
16
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
17
17
  import { parseStrictNonNegativeInteger, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
18
18
  import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
@@ -25,7 +25,7 @@ import { createHmac, randomBytes } from "node:crypto";
25
25
  import { fileExists, safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
26
26
  import { clearAccountEntryFields } from "openclaw/plugin-sdk/channel-plugin-common";
27
27
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2 } from "openclaw/plugin-sdk/account-id";
28
- import { DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, ReplyRuntimeConfigSchemaShape, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
28
+ import { DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, ReplyRuntimeConfigSchemaShape, ReplyToModeSchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
29
29
  import { formatAllowFromLowercase, resolveAllowlistMatchByCandidates } from "openclaw/plugin-sdk/allow-from";
30
30
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter, createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
31
31
  import { requireChannelOpenAllowFrom, resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared";
@@ -40,7 +40,7 @@ import { buildChannelKeyCandidates, normalizeChannelSlug, resolveChannelEntryMat
40
40
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
41
41
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
42
42
  import { createServer } from "node:http";
43
- import { WEBHOOK_RATE_LIMIT_DEFAULTS, createAuthRateLimiter, isRequestBodyLimitError, readRequestBodyWithLimit, requestBodyErrorToText } from "openclaw/plugin-sdk/webhook-ingress";
43
+ import { WEBHOOK_RATE_LIMIT_DEFAULTS, createAuthRateLimiter, isRequestBodyLimitError, readRequestBodyWithLimit, requestBodyErrorToText, resolveRequestClientIp } from "openclaw/plugin-sdk/webhook-ingress";
44
44
  import { isRecord } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
45
45
  import { jsonResult, readStringParam, resolveReactionMessageId } from "openclaw/plugin-sdk/channel-actions";
46
46
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, buildOutboundBaseSessionKey, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/routing";
@@ -97,18 +97,26 @@ function listNextcloudTalkAccountIds(cfg) {
97
97
  debugAccounts("listNextcloudTalkAccountIds", ids);
98
98
  return ids;
99
99
  }
100
- function resolveNextcloudTalkSecret(cfg, opts) {
101
- const resolvedAccountId = opts.accountId ?? resolveDefaultNextcloudTalkAccountId(cfg);
102
- const merged = mergeNextcloudTalkAccountConfig(cfg, resolvedAccountId);
100
+ function resolveNextcloudTalkSecret(accountId, merged) {
101
+ const configuredSecret = resolveSecretInputString({
102
+ value: merged.botSecret,
103
+ path: `channels.nextcloud-talk.accounts.${accountId}.botSecret`,
104
+ mode: "inspect"
105
+ });
106
+ if (configuredSecret.status === "configured_unavailable") return {
107
+ secret: "",
108
+ source: "config",
109
+ status: "configured_unavailable"
110
+ };
103
111
  const envSecret = normalizeOptionalString(process.env.NEXTCLOUD_TALK_BOT_SECRET);
104
- if (envSecret && resolvedAccountId === DEFAULT_ACCOUNT_ID) return {
112
+ if (envSecret && accountId === DEFAULT_ACCOUNT_ID) return {
105
113
  secret: envSecret,
106
114
  source: "env",
107
115
  status: "available"
108
116
  };
109
117
  const botSecretFile = normalizeOptionalString(merged.botSecretFile);
110
118
  if (botSecretFile) {
111
- const result = tryReadSecretFileSync(botSecretFile, "Nextcloud Talk bot secret file", { rejectSymlink: true }, { configPath: `channels.nextcloud-talk.accounts.${resolvedAccountId}.botSecretFile` });
119
+ const result = tryReadSecretFileSync(botSecretFile, "Nextcloud Talk bot secret file", { rejectSymlink: true }, { configPath: `channels.nextcloud-talk.accounts.${accountId}.botSecretFile` });
112
120
  return result.status === "available" ? {
113
121
  secret: result.value,
114
122
  source: "secretFile",
@@ -120,12 +128,8 @@ function resolveNextcloudTalkSecret(cfg, opts) {
120
128
  diagnostic: result.diagnostic
121
129
  };
122
130
  }
123
- const inlineSecret = normalizeResolvedSecretInputString({
124
- value: merged.botSecret,
125
- path: `channels.nextcloud-talk.accounts.${resolvedAccountId}.botSecret`
126
- });
127
- if (inlineSecret) return {
128
- secret: inlineSecret,
131
+ if (configuredSecret.status === "available") return {
132
+ secret: configuredSecret.value,
129
133
  source: "config",
130
134
  status: "available"
131
135
  };
@@ -142,14 +146,7 @@ function resolveNextcloudTalkAccount(params) {
142
146
  const merged = mergeNextcloudTalkAccountConfig(params.cfg, accountId);
143
147
  const accountEnabled = merged.enabled !== false;
144
148
  const enabled = baseEnabled && accountEnabled;
145
- const secretResolution = resolveNextcloudTalkSecret(params.cfg, { accountId });
146
- const apiCredentialResolution = resolveNextcloudTalkApiCredentialsResult({
147
- apiUser: merged.apiUser,
148
- apiPassword: merged.apiPassword,
149
- apiPasswordFile: merged.apiPasswordFile,
150
- configPath: `channels.nextcloud-talk.accounts.${accountId}.apiPasswordFile`
151
- });
152
- const diagnostics = [secretResolution.diagnostic, apiCredentialResolution.status === "configured_unavailable" ? apiCredentialResolution.diagnostic : void 0].filter((diagnostic) => Boolean(diagnostic));
149
+ const secretResolution = resolveNextcloudTalkSecret(accountId, merged);
153
150
  const baseUrl = merged.baseUrl?.trim()?.replace(/\/$/, "") ?? "";
154
151
  debugAccounts("resolve", {
155
152
  accountId,
@@ -165,8 +162,7 @@ function resolveNextcloudTalkAccount(params) {
165
162
  secret: secretResolution.secret,
166
163
  secretSource: secretResolution.source,
167
164
  tokenStatus: secretResolution.status,
168
- apiCredentialStatus: apiCredentialResolution.status,
169
- ...diagnostics.length > 0 ? { credentialDiagnostics: diagnostics } : {},
165
+ ...secretResolution.diagnostic ? { credentialDiagnostics: [secretResolution.diagnostic] } : {},
170
166
  config: merged
171
167
  };
172
168
  };
@@ -178,6 +174,21 @@ function resolveNextcloudTalkAccount(params) {
178
174
  resolveDefaultAccountId: () => resolveDefaultNextcloudTalkAccountId(params.cfg)
179
175
  });
180
176
  }
177
+ function inspectNextcloudTalkAccount(params) {
178
+ const account = resolveNextcloudTalkAccount(params);
179
+ const apiCredentialResolution = resolveNextcloudTalkApiCredentialsResult({
180
+ apiUser: account.config.apiUser,
181
+ apiPassword: account.config.apiPassword,
182
+ apiPasswordFile: account.config.apiPasswordFile,
183
+ configPath: `channels.nextcloud-talk.accounts.${account.accountId}.apiPasswordFile`
184
+ });
185
+ const credentialDiagnostics = [...account.credentialDiagnostics ?? [], ...apiCredentialResolution.status === "configured_unavailable" ? [apiCredentialResolution.diagnostic] : []];
186
+ return {
187
+ ...account,
188
+ apiCredentialStatus: apiCredentialResolution.status,
189
+ ...credentialDiagnostics.length > 0 ? { credentialDiagnostics } : {}
190
+ };
191
+ }
181
192
  //#endregion
182
193
  //#region extensions/nextcloud-talk/src/approval-auth.ts
183
194
  function normalizeNextcloudTalkApproverId(value) {
@@ -362,6 +373,7 @@ const nextcloudTalkConfigAdapter = createScopedChannelConfigAdapter({
362
373
  sectionKey: "nextcloud-talk",
363
374
  listAccountIds: listNextcloudTalkAccountIds,
364
375
  resolveAccount: adaptScopedAccountAccessor(resolveNextcloudTalkAccount),
376
+ inspectAccount: adaptScopedAccountAccessor(inspectNextcloudTalkAccount),
365
377
  defaultAccountId: resolveDefaultNextcloudTalkAccountId,
366
378
  clearBaseFields: [
367
379
  "botSecret",
@@ -397,6 +409,7 @@ const NextcloudTalkConfigSchema = buildMultiAccountChannelSchema(z.object({
397
409
  name: z.string().optional(),
398
410
  enabled: z.boolean().optional(),
399
411
  configWrites: z.boolean().optional(),
412
+ replyToMode: ReplyToModeSchema.optional(),
400
413
  markdown: MarkdownConfigSchema,
401
414
  baseUrl: z.string().optional(),
402
415
  botSecret: buildSecretInputSchema().optional(),
@@ -1352,7 +1365,7 @@ function createNextcloudTalkWebhookServer(opts) {
1352
1365
  res.end();
1353
1366
  return;
1354
1367
  }
1355
- const clientIp = req.socket.remoteAddress ?? "unknown";
1368
+ const clientIp = resolveRequestClientIp(req, opts.trustedProxies, opts.allowRealIpFallback) ?? req.socket.remoteAddress ?? "unknown";
1356
1369
  if (!webhookAuthRateLimiter.check(clientIp, WEBHOOK_AUTH_RATE_LIMIT_SCOPE).allowed) {
1357
1370
  res.writeHead(429);
1358
1371
  res.end("Too Many Requests");
@@ -1410,6 +1423,7 @@ function createNextcloudTalkWebhookServer(opts) {
1410
1423
  const stop = async () => {
1411
1424
  stopRequested = true;
1412
1425
  await closeIfListening();
1426
+ webhookAuthRateLimiter.dispose();
1413
1427
  };
1414
1428
  const start = () => {
1415
1429
  if (stopRequested) return Promise.resolve();
@@ -1647,6 +1661,8 @@ async function monitorNextcloudTalkProvider(opts) {
1647
1661
  onError: (error) => {
1648
1662
  logger.error(`[nextcloud-talk:${account.accountId}] webhook error: ${error.message}`);
1649
1663
  },
1664
+ trustedProxies: cfg.gateway?.trustedProxies,
1665
+ allowRealIpFallback: cfg.gateway?.allowRealIpFallback,
1650
1666
  abortSignal: opts.abortSignal
1651
1667
  });
1652
1668
  let stopPromise;
@@ -2192,6 +2208,12 @@ const collectNextcloudTalkSecurityWarnings = createAllowlistProviderRouteAllowli
2192
2208
  groupAllowFromPath: "channels.nextcloud-talk.groupAllowFrom"
2193
2209
  }
2194
2210
  });
2211
+ const collectNextcloudTalkOpenGroupFindings = createConditionalWarningCollector.findings({
2212
+ collectWarnings: collectNextcloudTalkSecurityWarnings,
2213
+ checkId: "channels.nextcloud-talk.groups.open",
2214
+ severity: "critical",
2215
+ title: "Nextcloud Talk security warning"
2216
+ });
2195
2217
  const nextcloudTalkPlugin = createChatChannelPlugin({
2196
2218
  base: {
2197
2219
  id: "nextcloud-talk",
@@ -2288,7 +2310,7 @@ const nextcloudTalkPlugin = createChatChannelPlugin({
2288
2310
  } },
2289
2311
  security: {
2290
2312
  ...nextcloudTalkSecurityAdapter,
2291
- collectWarnings: collectNextcloudTalkSecurityWarnings
2313
+ collectWarnings: collectNextcloudTalkOpenGroupFindings
2292
2314
  },
2293
2315
  outbound: {
2294
2316
  base: {
@@ -0,0 +1,25 @@
1
+ import { DEFAULT_ACCOUNT_ID, hasConfiguredAccountValue, mergeAccountConfig } from "openclaw/plugin-sdk/account-core";
2
+ //#region extensions/nextcloud-talk/configured-state.ts
3
+ function hasConfiguredNextcloudAccount(account, env) {
4
+ return Boolean(account?.baseUrl?.trim() && (hasConfiguredAccountValue(account.botSecret) || hasConfiguredAccountValue(account.botSecretFile) || hasConfiguredAccountValue(env.NEXTCLOUD_TALK_BOT_SECRET)));
5
+ }
6
+ /** Require a Nextcloud server plus its account-owned bot credential. */
7
+ function hasConfiguredNextcloudTalkChannelState(params) {
8
+ const channel = params.cfg.channels?.["nextcloud-talk"];
9
+ if (channel?.enabled === false) return false;
10
+ const defaultAccount = channel?.accounts?.[DEFAULT_ACCOUNT_ID];
11
+ if (defaultAccount?.enabled !== false) {
12
+ if (hasConfiguredNextcloudAccount(defaultAccount ? mergeAccountConfig({
13
+ channelConfig: channel,
14
+ accountConfig: defaultAccount,
15
+ omitKeys: ["defaultAccount"]
16
+ }) : channel, params.env ?? process.env)) return true;
17
+ }
18
+ return Object.entries(channel?.accounts ?? {}).some(([accountId, account]) => accountId !== DEFAULT_ACCOUNT_ID && account.enabled !== false && hasConfiguredNextcloudAccount(mergeAccountConfig({
19
+ channelConfig: channel,
20
+ accountConfig: account,
21
+ omitKeys: ["defaultAccount"]
22
+ }), {}));
23
+ }
24
+ //#endregion
25
+ export { hasConfiguredNextcloudTalkChannelState };
@@ -32,6 +32,26 @@
32
32
  "configWrites": {
33
33
  "type": "boolean"
34
34
  },
35
+ "replyToMode": {
36
+ "anyOf": [
37
+ {
38
+ "type": "string",
39
+ "const": "off"
40
+ },
41
+ {
42
+ "type": "string",
43
+ "const": "first"
44
+ },
45
+ {
46
+ "type": "string",
47
+ "const": "all"
48
+ },
49
+ {
50
+ "type": "string",
51
+ "const": "batched"
52
+ }
53
+ ]
54
+ },
35
55
  "markdown": {
36
56
  "type": "object",
37
57
  "properties": {
@@ -479,6 +499,26 @@
479
499
  "configWrites": {
480
500
  "type": "boolean"
481
501
  },
502
+ "replyToMode": {
503
+ "anyOf": [
504
+ {
505
+ "type": "string",
506
+ "const": "off"
507
+ },
508
+ {
509
+ "type": "string",
510
+ "const": "first"
511
+ },
512
+ {
513
+ "type": "string",
514
+ "const": "all"
515
+ },
516
+ {
517
+ "type": "string",
518
+ "const": "batched"
519
+ }
520
+ ]
521
+ },
482
522
  "markdown": {
483
523
  "type": "object",
484
524
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/nextcloud-talk",
3
- "version": "2026.8.1-beta.2",
3
+ "version": "2026.9.1-beta.1",
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.8.1-beta.2"
11
+ "openclaw": ">=2026.9.1-beta.1"
12
12
  },
13
13
  "peerDependenciesMeta": {
14
14
  "openclaw": {
@@ -31,7 +31,9 @@
31
31
  "NEXTCLOUD_TALK_BOT_SECRET",
32
32
  "NEXTCLOUD_TALK_API_PASSWORD"
33
33
  ]
34
- }
34
+ },
35
+ "specifier": "./dist/configured-state.js",
36
+ "exportName": "hasConfiguredNextcloudTalkChannelState"
35
37
  },
36
38
  "label": "Nextcloud Talk",
37
39
  "selectionLabel": "Nextcloud Talk (self-hosted)",
@@ -127,10 +129,10 @@
127
129
  "minHostVersion": ">=2026.4.10"
128
130
  },
129
131
  "compat": {
130
- "pluginApi": ">=2026.8.1-beta.2"
132
+ "pluginApi": ">=2026.9.1-beta.1"
131
133
  },
132
134
  "build": {
133
- "openclawVersion": "2026.8.1-beta.2"
135
+ "openclawVersion": "2026.9.1-beta.1"
134
136
  },
135
137
  "release": {
136
138
  "publishToClawHub": true,