@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-
|
|
1
|
+
import { t as nextcloudTalkPlugin } from "./channel-tgvjpexV.js";
|
|
2
2
|
export { nextcloudTalkPlugin };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as nextcloudTalkPlugin } from "./channel-
|
|
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(
|
|
101
|
-
const
|
|
102
|
-
|
|
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 &&
|
|
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.${
|
|
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
|
-
|
|
124
|
-
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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 };
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
132
|
+
"pluginApi": ">=2026.9.1-beta.1"
|
|
131
133
|
},
|
|
132
134
|
"build": {
|
|
133
|
-
"openclawVersion": "2026.
|
|
135
|
+
"openclawVersion": "2026.9.1-beta.1"
|
|
134
136
|
},
|
|
135
137
|
"release": {
|
|
136
138
|
"publishToClawHub": true,
|