@openclaw/nextcloud-talk 2026.7.2-beta.7 → 2026.8.1-beta.3
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 +1 -1
- package/dist/{channel-B9yquEb1.js → channel-gYP-jQPr.js} +100 -86
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{doctor-contract-fNhK1wag.js → doctor-contract-Bda_xc6V.js} +1 -2
- package/dist/doctor-contract-api.js +1 -1
- package/dist/secret-contract-Bvm0PmP3.js +15 -0
- package/dist/secret-contract-api.js +1 -1
- package/openclaw.plugin.json +101 -0
- package/package.json +8 -5
- package/dist/secret-contract-DGgvfVqm.js +0 -45
package/dist/api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as nextcloudTalkPlugin } from "./channel-
|
|
1
|
+
import { t as nextcloudTalkPlugin } from "./channel-gYP-jQPr.js";
|
|
2
2
|
export { nextcloudTalkPlugin };
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { a as fetchWithSsrFGuard, c as resolveDefaultGroupPolicy, i as deliverFormattedTextWithAttachments, l as warnMissingProviderGroupPolicyFallbackOnce, o as logInboundDrop, r as createChannelPairingController, s as resolveAllowlistProviderRuntimeGroupPolicy, t as GROUP_POLICY_BLOCKED_LABEL, u as getNextcloudTalkRuntime } from "./runtime-api-BKsxTw4J.js";
|
|
2
|
-
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-
|
|
3
|
-
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-
|
|
2
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-Bda_xc6V.js";
|
|
3
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-Bvm0PmP3.js";
|
|
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";
|
|
11
|
-
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
11
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, readNonEmptyStringPreservingWhitespace } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
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";
|
|
19
|
-
import {
|
|
19
|
+
import { resolveChannelInboundRouteEnvelope } from "openclaw/plugin-sdk/channel-inbound";
|
|
20
20
|
import { bindIngressLifecycleToReplyOptions, createAccountStatusSink, createChannelIngressMonitor, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter, resolveChannelStreamingBlockEnabled, runPassiveAccountLifecycle } from "openclaw/plugin-sdk/channel-outbound";
|
|
21
21
|
import { ssrfPolicyFromPrivateNetworkOptIn, ssrfPolicyFromPrivateNetworkOptIn as ssrfPolicyFromPrivateNetworkOptIn$1 } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
22
22
|
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
@@ -34,12 +34,13 @@ import os from "node:os";
|
|
|
34
34
|
import path from "node:path";
|
|
35
35
|
import { migratePersistentDedupeLegacyJsonFile, resolvePersistentDedupePluginStateNamespace } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
36
36
|
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
37
|
+
import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
37
38
|
import { channelIngressRoutes, resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
38
39
|
import { buildChannelKeyCandidates, normalizeChannelSlug, resolveChannelEntryMatchWithFallback } from "openclaw/plugin-sdk/channel-targets";
|
|
39
40
|
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
40
41
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
41
42
|
import { createServer } from "node:http";
|
|
42
|
-
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";
|
|
43
44
|
import { isRecord } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
44
45
|
import { jsonResult, readStringParam, resolveReactionMessageId } from "openclaw/plugin-sdk/channel-actions";
|
|
45
46
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, buildOutboundBaseSessionKey, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/routing";
|
|
@@ -96,18 +97,26 @@ function listNextcloudTalkAccountIds(cfg) {
|
|
|
96
97
|
debugAccounts("listNextcloudTalkAccountIds", ids);
|
|
97
98
|
return ids;
|
|
98
99
|
}
|
|
99
|
-
function resolveNextcloudTalkSecret(
|
|
100
|
-
const
|
|
101
|
-
|
|
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
|
+
};
|
|
102
111
|
const envSecret = normalizeOptionalString(process.env.NEXTCLOUD_TALK_BOT_SECRET);
|
|
103
|
-
if (envSecret &&
|
|
112
|
+
if (envSecret && accountId === DEFAULT_ACCOUNT_ID) return {
|
|
104
113
|
secret: envSecret,
|
|
105
114
|
source: "env",
|
|
106
115
|
status: "available"
|
|
107
116
|
};
|
|
108
117
|
const botSecretFile = normalizeOptionalString(merged.botSecretFile);
|
|
109
118
|
if (botSecretFile) {
|
|
110
|
-
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` });
|
|
111
120
|
return result.status === "available" ? {
|
|
112
121
|
secret: result.value,
|
|
113
122
|
source: "secretFile",
|
|
@@ -119,12 +128,8 @@ function resolveNextcloudTalkSecret(cfg, opts) {
|
|
|
119
128
|
diagnostic: result.diagnostic
|
|
120
129
|
};
|
|
121
130
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
path: `channels.nextcloud-talk.accounts.${resolvedAccountId}.botSecret`
|
|
125
|
-
});
|
|
126
|
-
if (inlineSecret) return {
|
|
127
|
-
secret: inlineSecret,
|
|
131
|
+
if (configuredSecret.status === "available") return {
|
|
132
|
+
secret: configuredSecret.value,
|
|
128
133
|
source: "config",
|
|
129
134
|
status: "available"
|
|
130
135
|
};
|
|
@@ -141,7 +146,7 @@ function resolveNextcloudTalkAccount(params) {
|
|
|
141
146
|
const merged = mergeNextcloudTalkAccountConfig(params.cfg, accountId);
|
|
142
147
|
const accountEnabled = merged.enabled !== false;
|
|
143
148
|
const enabled = baseEnabled && accountEnabled;
|
|
144
|
-
const secretResolution = resolveNextcloudTalkSecret(
|
|
149
|
+
const secretResolution = resolveNextcloudTalkSecret(accountId, merged);
|
|
145
150
|
const apiCredentialResolution = resolveNextcloudTalkApiCredentialsResult({
|
|
146
151
|
apiUser: merged.apiUser,
|
|
147
152
|
apiPassword: merged.apiPassword,
|
|
@@ -395,6 +400,7 @@ dangerouslyAllowPrivateNetwork: z.boolean().optional() }).strict().optional();
|
|
|
395
400
|
const NextcloudTalkConfigSchema = buildMultiAccountChannelSchema(z.object({
|
|
396
401
|
name: z.string().optional(),
|
|
397
402
|
enabled: z.boolean().optional(),
|
|
403
|
+
configWrites: z.boolean().optional(),
|
|
398
404
|
markdown: MarkdownConfigSchema,
|
|
399
405
|
baseUrl: z.string().optional(),
|
|
400
406
|
botSecret: buildSecretInputSchema().optional(),
|
|
@@ -593,10 +599,6 @@ function cacheRoomInfo(key, value) {
|
|
|
593
599
|
roomCache.set(key, value);
|
|
594
600
|
pruneMapToMaxSize(roomCache, ROOM_CACHE_MAX_ENTRIES);
|
|
595
601
|
}
|
|
596
|
-
function coerceRoomType(value) {
|
|
597
|
-
if (typeof value === "number" && Number.isSafeInteger(value) && value > 0) return value;
|
|
598
|
-
return parseStrictPositiveInteger(value);
|
|
599
|
-
}
|
|
600
602
|
function resolveRoomKindFromType(type) {
|
|
601
603
|
if (!type) return;
|
|
602
604
|
if (type === 1 || type === 5 || type === 6) return "direct";
|
|
@@ -648,7 +650,7 @@ async function resolveNextcloudTalkRoomKind(params) {
|
|
|
648
650
|
runtime?.log?.(`nextcloud-talk: room lookup failed (${response.status}) token=${roomToken}`);
|
|
649
651
|
return;
|
|
650
652
|
}
|
|
651
|
-
const kind = resolveRoomKindFromType(
|
|
653
|
+
const kind = resolveRoomKindFromType(parseStrictPositiveInteger((await readProviderJsonResponse(response, "Nextcloud Talk room info failed")).ocs?.data?.type));
|
|
652
654
|
cacheRoomInfo(key, {
|
|
653
655
|
fetchedAt: Date.now(),
|
|
654
656
|
kind
|
|
@@ -987,7 +989,7 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
987
989
|
const allowFrom = normalizeStringEntries(account.config.allowFrom);
|
|
988
990
|
const outerGroupAllowFrom = account.config.groupAllowFrom?.length ? normalizeStringEntries(account.config.groupAllowFrom) : allowFrom;
|
|
989
991
|
const roomAllowFrom = normalizeStringEntries(roomConfig?.allowFrom);
|
|
990
|
-
const resolveAccess = async (wasMentioned) => await resolveStableChannelMessageIngress({
|
|
992
|
+
const resolveAccess = async (wasMentioned, contextBinding) => await resolveStableChannelMessageIngress({
|
|
991
993
|
channelId: CHANNEL_ID,
|
|
992
994
|
accountId: account.accountId,
|
|
993
995
|
identity: {
|
|
@@ -1003,6 +1005,7 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1003
1005
|
kind: isGroup ? "group" : "direct",
|
|
1004
1006
|
id: isGroup ? roomToken : senderId
|
|
1005
1007
|
},
|
|
1008
|
+
contextBinding,
|
|
1006
1009
|
route: roomRoutes({
|
|
1007
1010
|
isGroup,
|
|
1008
1011
|
groupPolicy,
|
|
@@ -1090,11 +1093,6 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1090
1093
|
}
|
|
1091
1094
|
const mentionRegexes = core.channel.mentions.buildMentionRegexes(config);
|
|
1092
1095
|
const wasMentioned = mentionRegexes.length ? core.channel.mentions.matchesMentionPatterns(rawBody, mentionRegexes) : false;
|
|
1093
|
-
if (isGroup) access = await resolveAccess(wasMentioned);
|
|
1094
|
-
if (isGroup && access.activationAccess.shouldSkip) {
|
|
1095
|
-
runtime.log?.(`nextcloud-talk: drop room ${roomToken} (no mention)`);
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
1096
|
const { route, buildEnvelope } = resolveChannelInboundRouteEnvelope({
|
|
1099
1097
|
cfg: config,
|
|
1100
1098
|
channel: CHANNEL_ID,
|
|
@@ -1104,6 +1102,16 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1104
1102
|
id: isGroup ? roomToken : senderId
|
|
1105
1103
|
}
|
|
1106
1104
|
});
|
|
1105
|
+
access = await resolveAccess(isGroup ? wasMentioned : void 0, {
|
|
1106
|
+
agentId: route.agentId,
|
|
1107
|
+
sessionKey: route.sessionKey,
|
|
1108
|
+
messageId: message.messageId,
|
|
1109
|
+
inboundEventKind: "user_request"
|
|
1110
|
+
});
|
|
1111
|
+
if (access.ingress.admission !== "dispatch") {
|
|
1112
|
+
runtime.log?.(isGroup && access.activationAccess.shouldSkip ? `nextcloud-talk: drop room ${roomToken} (no mention)` : `nextcloud-talk: drop ${isGroup ? "room" : "DM"} ${roomToken} (authorization changed)`);
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1107
1115
|
const fromLabel = isGroup ? `room:${roomName || roomToken}` : senderName || `user:${senderId}`;
|
|
1108
1116
|
const body = buildEnvelope({
|
|
1109
1117
|
channel: "Nextcloud Talk",
|
|
@@ -1113,7 +1121,8 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1113
1121
|
});
|
|
1114
1122
|
const groupSystemPrompt = normalizeOptionalString(roomConfig?.systemPrompt);
|
|
1115
1123
|
const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(account.config);
|
|
1116
|
-
const ctxPayload =
|
|
1124
|
+
const ctxPayload = core.channel.inbound.buildContext({
|
|
1125
|
+
channelIngress: access,
|
|
1117
1126
|
channel: CHANNEL_ID,
|
|
1118
1127
|
accountId: route.accountId,
|
|
1119
1128
|
messageId: message.messageId,
|
|
@@ -1125,7 +1134,7 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1125
1134
|
},
|
|
1126
1135
|
conversation: {
|
|
1127
1136
|
kind: isGroup ? "group" : "direct",
|
|
1128
|
-
id: roomToken,
|
|
1137
|
+
id: isGroup ? roomToken : senderId,
|
|
1129
1138
|
label: fromLabel
|
|
1130
1139
|
},
|
|
1131
1140
|
route: {
|
|
@@ -1276,10 +1285,6 @@ const WEBHOOK_ERRORS = {
|
|
|
1276
1285
|
payloadTooLarge: "Payload too large",
|
|
1277
1286
|
internalServerError: "Internal server error"
|
|
1278
1287
|
};
|
|
1279
|
-
function formatError(err) {
|
|
1280
|
-
if (err instanceof Error) return err.message;
|
|
1281
|
-
return typeof err === "string" ? err : JSON.stringify(err);
|
|
1282
|
-
}
|
|
1283
1288
|
function writeJsonResponse(res, status, body) {
|
|
1284
1289
|
if (body) {
|
|
1285
1290
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
@@ -1351,7 +1356,7 @@ function createNextcloudTalkWebhookServer(opts) {
|
|
|
1351
1356
|
res.end();
|
|
1352
1357
|
return;
|
|
1353
1358
|
}
|
|
1354
|
-
const clientIp = req.socket.remoteAddress ?? "unknown";
|
|
1359
|
+
const clientIp = resolveRequestClientIp(req, opts.trustedProxies, opts.allowRealIpFallback) ?? req.socket.remoteAddress ?? "unknown";
|
|
1355
1360
|
if (!webhookAuthRateLimiter.check(clientIp, WEBHOOK_AUTH_RATE_LIMIT_SCOPE).allowed) {
|
|
1356
1361
|
res.writeHead(429);
|
|
1357
1362
|
res.end("Too Many Requests");
|
|
@@ -1388,7 +1393,7 @@ function createNextcloudTalkWebhookServer(opts) {
|
|
|
1388
1393
|
writeWebhookError(res, 400, WEBHOOK_ERRORS.invalidPayloadFormat);
|
|
1389
1394
|
return;
|
|
1390
1395
|
}
|
|
1391
|
-
const error = err instanceof Error ? err : new Error(
|
|
1396
|
+
const error = err instanceof Error ? err : new Error(formatErrorMessage(err));
|
|
1392
1397
|
onError?.(error);
|
|
1393
1398
|
writeWebhookError(res, 500, WEBHOOK_ERRORS.internalServerError);
|
|
1394
1399
|
}
|
|
@@ -1536,12 +1541,12 @@ function createNextcloudTalkWebhookSpool(options) {
|
|
|
1536
1541
|
await options.deliver(message, lifecycle);
|
|
1537
1542
|
},
|
|
1538
1543
|
pollIntervalMs: options.pollIntervalMs ?? NEXTCLOUD_TALK_INGRESS_POLL_INTERVAL_MS,
|
|
1539
|
-
waitForDeliveryIdleBeforeRepump: true,
|
|
1540
1544
|
retention: {
|
|
1541
1545
|
completedMaxEntries: 1e4,
|
|
1542
1546
|
failedMaxEntries: 1e4
|
|
1543
1547
|
},
|
|
1544
1548
|
drain: {
|
|
1549
|
+
startLimit: 32,
|
|
1545
1550
|
resolveNonRetryableFailure,
|
|
1546
1551
|
...options.adoptionStallTimeoutMs === void 0 ? {} : { adoptionStallTimeoutMs: options.adoptionStallTimeoutMs },
|
|
1547
1552
|
onLog: (message) => options.runtime.log?.(`nextcloud-talk ${message}`)
|
|
@@ -1646,6 +1651,8 @@ async function monitorNextcloudTalkProvider(opts) {
|
|
|
1646
1651
|
onError: (error) => {
|
|
1647
1652
|
logger.error(`[nextcloud-talk:${account.accountId}] webhook error: ${error.message}`);
|
|
1648
1653
|
},
|
|
1654
|
+
trustedProxies: cfg.gateway?.trustedProxies,
|
|
1655
|
+
allowRealIpFallback: cfg.gateway?.allowRealIpFallback,
|
|
1649
1656
|
abortSignal: opts.abortSignal
|
|
1650
1657
|
});
|
|
1651
1658
|
let stopPromise;
|
|
@@ -1672,6 +1679,7 @@ async function monitorNextcloudTalkProvider(opts) {
|
|
|
1672
1679
|
await stop();
|
|
1673
1680
|
return { stop };
|
|
1674
1681
|
}
|
|
1682
|
+
opts.statusSink?.(channelReadyPatch());
|
|
1675
1683
|
const publicUrl = account.config.webhookPublicUrl ?? `http://${host === "0.0.0.0" ? "localhost" : host}:${port}${path}`;
|
|
1676
1684
|
logger.info(`[nextcloud-talk:${account.accountId}] webhook listening on ${publicUrl}`);
|
|
1677
1685
|
return { stop };
|
|
@@ -1857,9 +1865,6 @@ function resolveNextcloudTalkOutboundSessionRoute(params) {
|
|
|
1857
1865
|
//#region extensions/nextcloud-talk/src/setup-core.ts
|
|
1858
1866
|
const t$1 = createSetupTranslator$1();
|
|
1859
1867
|
const channel$1 = "nextcloud-talk";
|
|
1860
|
-
function readOptionalString(value) {
|
|
1861
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1862
|
-
}
|
|
1863
1868
|
function normalizeNextcloudTalkBaseUrl(value) {
|
|
1864
1869
|
return value?.trim().replace(/\/+$/, "") ?? "";
|
|
1865
1870
|
}
|
|
@@ -1924,46 +1929,6 @@ const nextcloudTalkDmPolicy = createChannelDmPolicy({
|
|
|
1924
1929
|
applyPatch: ({ cfg, account, patch }) => setNextcloudTalkAccountConfig(cfg, account.accountId, patch),
|
|
1925
1930
|
promptAllowFrom: promptNextcloudTalkAllowFromForAccount
|
|
1926
1931
|
});
|
|
1927
|
-
const nextcloudTalkSetupAdapter = {
|
|
1928
|
-
singleAccountKeysToMove: ["rooms"],
|
|
1929
|
-
resolveAccountId: ({ accountId }) => normalizeAccountId$1(accountId),
|
|
1930
|
-
prepareAccountConfigInput: ({ input }) => {
|
|
1931
|
-
const setupInput = input;
|
|
1932
|
-
return {
|
|
1933
|
-
...setupInput,
|
|
1934
|
-
baseUrl: setupInput.baseUrl ?? readOptionalString(setupInput.url),
|
|
1935
|
-
secret: setupInput.secret ?? readOptionalString(setupInput.token) ?? readOptionalString(setupInput.password),
|
|
1936
|
-
secretFile: setupInput.secretFile ?? readOptionalString(setupInput.tokenFile)
|
|
1937
|
-
};
|
|
1938
|
-
},
|
|
1939
|
-
applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
|
|
1940
|
-
cfg,
|
|
1941
|
-
channelKey: channel$1,
|
|
1942
|
-
accountId,
|
|
1943
|
-
name
|
|
1944
|
-
}),
|
|
1945
|
-
validateInput: createSetupInputPresenceValidator({
|
|
1946
|
-
defaultAccountOnlyEnvError: "NEXTCLOUD_TALK_BOT_SECRET can only be used for the default account.",
|
|
1947
|
-
validate: ({ input }) => {
|
|
1948
|
-
const setupInput = input;
|
|
1949
|
-
if (!setupInput.useEnv && !setupInput.secret && !setupInput.secretFile) return "Nextcloud Talk requires bot secret or --secret-file (or --use-env).";
|
|
1950
|
-
if (!setupInput.baseUrl) return "Nextcloud Talk requires --base-url.";
|
|
1951
|
-
return null;
|
|
1952
|
-
}
|
|
1953
|
-
}),
|
|
1954
|
-
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
1955
|
-
const setupInput = input;
|
|
1956
|
-
return setNextcloudTalkAccountConfig(applyAccountNameToChannelSection({
|
|
1957
|
-
cfg,
|
|
1958
|
-
channelKey: channel$1,
|
|
1959
|
-
accountId,
|
|
1960
|
-
name: setupInput.name
|
|
1961
|
-
}), accountId, {
|
|
1962
|
-
baseUrl: normalizeNextcloudTalkBaseUrl(setupInput.baseUrl),
|
|
1963
|
-
...setupInput.useEnv ? {} : setupInput.secretFile ? { botSecretFile: setupInput.secretFile } : setupInput.secret ? { botSecret: setupInput.secret } : {}
|
|
1964
|
-
}, setupInput.useEnv ? ["botSecret", "botSecretFile"] : void 0);
|
|
1965
|
-
}
|
|
1966
|
-
};
|
|
1967
1932
|
const nextcloudTalkSetupContract = defineChannelSetupContract({
|
|
1968
1933
|
fields: {
|
|
1969
1934
|
baseUrl: {
|
|
@@ -2025,10 +1990,53 @@ const nextcloudTalkSetupContract = defineChannelSetupContract({
|
|
|
2025
1990
|
cli: {
|
|
2026
1991
|
flags: "--use-env",
|
|
2027
1992
|
description: "Use Nextcloud Talk environment credentials"
|
|
2028
|
-
}
|
|
1993
|
+
},
|
|
1994
|
+
envVars: ["NEXTCLOUD_TALK_BOT_SECRET"]
|
|
2029
1995
|
}
|
|
2030
1996
|
},
|
|
2031
|
-
legacyAdapter:
|
|
1997
|
+
legacyAdapter: {
|
|
1998
|
+
singleAccountKeysToMove: ["rooms"],
|
|
1999
|
+
resolveAccountId: ({ accountId }) => normalizeAccountId$1(accountId),
|
|
2000
|
+
prepareAccountConfigInput: ({ input }) => {
|
|
2001
|
+
const setupInput = input;
|
|
2002
|
+
return {
|
|
2003
|
+
...setupInput,
|
|
2004
|
+
baseUrl: setupInput.baseUrl ?? readNonEmptyStringPreservingWhitespace(setupInput.url),
|
|
2005
|
+
secret: setupInput.secret ?? readNonEmptyStringPreservingWhitespace(setupInput.token) ?? readNonEmptyStringPreservingWhitespace(setupInput.password),
|
|
2006
|
+
secretFile: setupInput.secretFile ?? readNonEmptyStringPreservingWhitespace(setupInput.tokenFile)
|
|
2007
|
+
};
|
|
2008
|
+
},
|
|
2009
|
+
applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
|
|
2010
|
+
cfg,
|
|
2011
|
+
channelKey: channel$1,
|
|
2012
|
+
accountId,
|
|
2013
|
+
name
|
|
2014
|
+
}),
|
|
2015
|
+
validateInput: createSetupInputPresenceValidator({
|
|
2016
|
+
defaultAccountOnlyEnvError: "NEXTCLOUD_TALK_BOT_SECRET can only be used for the default account.",
|
|
2017
|
+
validate: ({ input }) => {
|
|
2018
|
+
const setupInput = input;
|
|
2019
|
+
if (!setupInput.useEnv && !setupInput.secret && !setupInput.secretFile) return "Nextcloud Talk requires bot secret or --secret-file (or --use-env).";
|
|
2020
|
+
const normalizedBaseUrl = normalizeNextcloudTalkBaseUrl(setupInput.baseUrl);
|
|
2021
|
+
if (!normalizedBaseUrl) return "Nextcloud Talk requires --base-url.";
|
|
2022
|
+
const baseUrlError = validateNextcloudTalkBaseUrl(normalizedBaseUrl);
|
|
2023
|
+
if (baseUrlError) return baseUrlError;
|
|
2024
|
+
return null;
|
|
2025
|
+
}
|
|
2026
|
+
}),
|
|
2027
|
+
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
2028
|
+
const setupInput = input;
|
|
2029
|
+
return setNextcloudTalkAccountConfig(applyAccountNameToChannelSection({
|
|
2030
|
+
cfg,
|
|
2031
|
+
channelKey: channel$1,
|
|
2032
|
+
accountId,
|
|
2033
|
+
name: setupInput.name
|
|
2034
|
+
}), accountId, {
|
|
2035
|
+
baseUrl: normalizeNextcloudTalkBaseUrl(setupInput.baseUrl),
|
|
2036
|
+
...setupInput.useEnv ? {} : setupInput.secretFile ? { botSecretFile: setupInput.secretFile } : setupInput.secret ? { botSecret: setupInput.secret } : {}
|
|
2037
|
+
}, setupInput.useEnv ? ["botSecret", "botSecretFile"] : void 0);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2032
2040
|
});
|
|
2033
2041
|
//#endregion
|
|
2034
2042
|
//#region extensions/nextcloud-talk/src/setup-surface.ts
|
|
@@ -2190,6 +2198,12 @@ const collectNextcloudTalkSecurityWarnings = createAllowlistProviderRouteAllowli
|
|
|
2190
2198
|
groupAllowFromPath: "channels.nextcloud-talk.groupAllowFrom"
|
|
2191
2199
|
}
|
|
2192
2200
|
});
|
|
2201
|
+
const collectNextcloudTalkOpenGroupFindings = createConditionalWarningCollector.findings({
|
|
2202
|
+
collectWarnings: collectNextcloudTalkSecurityWarnings,
|
|
2203
|
+
checkId: "channels.nextcloud-talk.groups.open",
|
|
2204
|
+
severity: "critical",
|
|
2205
|
+
title: "Nextcloud Talk security warning"
|
|
2206
|
+
});
|
|
2193
2207
|
const nextcloudTalkPlugin = createChatChannelPlugin({
|
|
2194
2208
|
base: {
|
|
2195
2209
|
id: "nextcloud-talk",
|
|
@@ -2232,6 +2246,7 @@ const nextcloudTalkPlugin = createChatChannelPlugin({
|
|
|
2232
2246
|
"nc"
|
|
2233
2247
|
],
|
|
2234
2248
|
normalizeTarget: normalizeNextcloudTalkMessagingTarget,
|
|
2249
|
+
inferTargetChatType: ({ to }) => normalizeNextcloudTalkMessagingTarget(to) ? "group" : void 0,
|
|
2235
2250
|
resolveOutboundSessionRoute: (params) => resolveNextcloudTalkOutboundSessionRoute(params),
|
|
2236
2251
|
targetResolver: {
|
|
2237
2252
|
looksLikeId: looksLikeNextcloudTalkTargetId,
|
|
@@ -2242,7 +2257,6 @@ const nextcloudTalkPlugin = createChatChannelPlugin({
|
|
|
2242
2257
|
secretTargetRegistryEntries,
|
|
2243
2258
|
collectRuntimeConfigAssignments
|
|
2244
2259
|
},
|
|
2245
|
-
setup: nextcloudTalkSetupAdapter,
|
|
2246
2260
|
setupContract: nextcloudTalkSetupContract,
|
|
2247
2261
|
status: createComputedAccountStatusAdapter({
|
|
2248
2262
|
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID$2),
|
|
@@ -2286,7 +2300,7 @@ const nextcloudTalkPlugin = createChatChannelPlugin({
|
|
|
2286
2300
|
} },
|
|
2287
2301
|
security: {
|
|
2288
2302
|
...nextcloudTalkSecurityAdapter,
|
|
2289
|
-
collectWarnings:
|
|
2303
|
+
collectWarnings: collectNextcloudTalkOpenGroupFindings
|
|
2290
2304
|
},
|
|
2291
2305
|
outbound: {
|
|
2292
2306
|
base: {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as nextcloudTalkPlugin } from "./channel-
|
|
1
|
+
import { t as nextcloudTalkPlugin } from "./channel-gYP-jQPr.js";
|
|
2
2
|
export { nextcloudTalkPlugin };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { createLegacyPrivateNetworkDoctorContract } from "openclaw/plugin-sdk/
|
|
2
|
-
import { defineChannelAliasMigration } from "openclaw/plugin-sdk/runtime-doctor";
|
|
1
|
+
import { createLegacyPrivateNetworkDoctorContract, defineChannelAliasMigration } from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
3
2
|
//#region extensions/nextcloud-talk/src/doctor-contract.ts
|
|
4
3
|
const networkContract = createLegacyPrivateNetworkDoctorContract({ channelKey: "nextcloud-talk" });
|
|
5
4
|
const streamingAliasMigration = defineChannelAliasMigration({
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-
|
|
1
|
+
import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-Bda_xc6V.js";
|
|
2
2
|
export { legacyConfigRules, normalizeCompatibilityConfig };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createSimpleChannelSecretContract } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
2
|
+
//#region extensions/nextcloud-talk/src/secret-contract.ts
|
|
3
|
+
const channelSecrets = createSimpleChannelSecretContract({
|
|
4
|
+
channelKey: "nextcloud-talk",
|
|
5
|
+
label: "Nextcloud Talk",
|
|
6
|
+
accountFields: ["apiPassword", "botSecret"],
|
|
7
|
+
channelFields: ["apiPassword", "botSecret"],
|
|
8
|
+
mode: {
|
|
9
|
+
kind: "surface-inheritance",
|
|
10
|
+
collectionFields: ["botSecret", "apiPassword"]
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
const { secretTargetRegistryEntries, collectRuntimeConfigAssignments } = channelSecrets;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { collectRuntimeConfigAssignments as n, secretTargetRegistryEntries as r, channelSecrets as t };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-
|
|
1
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-Bvm0PmP3.js";
|
|
2
2
|
export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nextcloud-talk",
|
|
3
|
+
"doctorContract": {
|
|
4
|
+
"configRepair": true
|
|
5
|
+
},
|
|
3
6
|
"name": "Nextcloud Talk",
|
|
4
7
|
"description": "OpenClaw Nextcloud Talk channel plugin for conversations.",
|
|
5
8
|
"icon": "https://cdn.simpleicons.org/nextcloud",
|
|
@@ -26,6 +29,9 @@
|
|
|
26
29
|
"enabled": {
|
|
27
30
|
"type": "boolean"
|
|
28
31
|
},
|
|
32
|
+
"configWrites": {
|
|
33
|
+
"type": "boolean"
|
|
34
|
+
},
|
|
29
35
|
"markdown": {
|
|
30
36
|
"type": "object",
|
|
31
37
|
"properties": {
|
|
@@ -74,6 +80,29 @@
|
|
|
74
80
|
],
|
|
75
81
|
"additionalProperties": false
|
|
76
82
|
},
|
|
83
|
+
{
|
|
84
|
+
"type": "object",
|
|
85
|
+
"properties": {
|
|
86
|
+
"source": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"const": "store"
|
|
89
|
+
},
|
|
90
|
+
"provider": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"pattern": "^[a-z][a-z0-9_-]{0,63}$"
|
|
93
|
+
},
|
|
94
|
+
"id": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"pattern": "^[A-Z][A-Z0-9_]{0,127}$"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"required": [
|
|
100
|
+
"source",
|
|
101
|
+
"provider",
|
|
102
|
+
"id"
|
|
103
|
+
],
|
|
104
|
+
"additionalProperties": false
|
|
105
|
+
},
|
|
77
106
|
{
|
|
78
107
|
"type": "object",
|
|
79
108
|
"properties": {
|
|
@@ -158,6 +187,29 @@
|
|
|
158
187
|
],
|
|
159
188
|
"additionalProperties": false
|
|
160
189
|
},
|
|
190
|
+
{
|
|
191
|
+
"type": "object",
|
|
192
|
+
"properties": {
|
|
193
|
+
"source": {
|
|
194
|
+
"type": "string",
|
|
195
|
+
"const": "store"
|
|
196
|
+
},
|
|
197
|
+
"provider": {
|
|
198
|
+
"type": "string",
|
|
199
|
+
"pattern": "^[a-z][a-z0-9_-]{0,63}$"
|
|
200
|
+
},
|
|
201
|
+
"id": {
|
|
202
|
+
"type": "string",
|
|
203
|
+
"pattern": "^[A-Z][A-Z0-9_]{0,127}$"
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"required": [
|
|
207
|
+
"source",
|
|
208
|
+
"provider",
|
|
209
|
+
"id"
|
|
210
|
+
],
|
|
211
|
+
"additionalProperties": false
|
|
212
|
+
},
|
|
161
213
|
{
|
|
162
214
|
"type": "object",
|
|
163
215
|
"properties": {
|
|
@@ -424,6 +476,9 @@
|
|
|
424
476
|
"enabled": {
|
|
425
477
|
"type": "boolean"
|
|
426
478
|
},
|
|
479
|
+
"configWrites": {
|
|
480
|
+
"type": "boolean"
|
|
481
|
+
},
|
|
427
482
|
"markdown": {
|
|
428
483
|
"type": "object",
|
|
429
484
|
"properties": {
|
|
@@ -472,6 +527,29 @@
|
|
|
472
527
|
],
|
|
473
528
|
"additionalProperties": false
|
|
474
529
|
},
|
|
530
|
+
{
|
|
531
|
+
"type": "object",
|
|
532
|
+
"properties": {
|
|
533
|
+
"source": {
|
|
534
|
+
"type": "string",
|
|
535
|
+
"const": "store"
|
|
536
|
+
},
|
|
537
|
+
"provider": {
|
|
538
|
+
"type": "string",
|
|
539
|
+
"pattern": "^[a-z][a-z0-9_-]{0,63}$"
|
|
540
|
+
},
|
|
541
|
+
"id": {
|
|
542
|
+
"type": "string",
|
|
543
|
+
"pattern": "^[A-Z][A-Z0-9_]{0,127}$"
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
"required": [
|
|
547
|
+
"source",
|
|
548
|
+
"provider",
|
|
549
|
+
"id"
|
|
550
|
+
],
|
|
551
|
+
"additionalProperties": false
|
|
552
|
+
},
|
|
475
553
|
{
|
|
476
554
|
"type": "object",
|
|
477
555
|
"properties": {
|
|
@@ -556,6 +634,29 @@
|
|
|
556
634
|
],
|
|
557
635
|
"additionalProperties": false
|
|
558
636
|
},
|
|
637
|
+
{
|
|
638
|
+
"type": "object",
|
|
639
|
+
"properties": {
|
|
640
|
+
"source": {
|
|
641
|
+
"type": "string",
|
|
642
|
+
"const": "store"
|
|
643
|
+
},
|
|
644
|
+
"provider": {
|
|
645
|
+
"type": "string",
|
|
646
|
+
"pattern": "^[a-z][a-z0-9_-]{0,63}$"
|
|
647
|
+
},
|
|
648
|
+
"id": {
|
|
649
|
+
"type": "string",
|
|
650
|
+
"pattern": "^[A-Z][A-Z0-9_]{0,127}$"
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
"required": [
|
|
654
|
+
"source",
|
|
655
|
+
"provider",
|
|
656
|
+
"id"
|
|
657
|
+
],
|
|
658
|
+
"additionalProperties": false
|
|
659
|
+
},
|
|
559
660
|
{
|
|
560
661
|
"type": "object",
|
|
561
662
|
"properties": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/nextcloud-talk",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.8.1-beta.3",
|
|
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.8.1-beta.3"
|
|
12
12
|
},
|
|
13
13
|
"peerDependenciesMeta": {
|
|
14
14
|
"openclaw": {
|
|
@@ -113,7 +113,10 @@
|
|
|
113
113
|
"cli": {
|
|
114
114
|
"flags": "--use-env",
|
|
115
115
|
"description": "Use Nextcloud Talk environment credentials"
|
|
116
|
-
}
|
|
116
|
+
},
|
|
117
|
+
"envVars": [
|
|
118
|
+
"NEXTCLOUD_TALK_BOT_SECRET"
|
|
119
|
+
]
|
|
117
120
|
}
|
|
118
121
|
]
|
|
119
122
|
}
|
|
@@ -124,10 +127,10 @@
|
|
|
124
127
|
"minHostVersion": ">=2026.4.10"
|
|
125
128
|
},
|
|
126
129
|
"compat": {
|
|
127
|
-
"pluginApi": ">=2026.
|
|
130
|
+
"pluginApi": ">=2026.8.1-beta.3"
|
|
128
131
|
},
|
|
129
132
|
"build": {
|
|
130
|
-
"openclawVersion": "2026.
|
|
133
|
+
"openclawVersion": "2026.8.1-beta.3"
|
|
131
134
|
},
|
|
132
135
|
"release": {
|
|
133
136
|
"publishToClawHub": true,
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { collectConditionalChannelFieldAssignments, createChannelSecretTargetRegistryEntries, getChannelSurface, hasOwnProperty } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
2
|
-
//#region extensions/nextcloud-talk/src/secret-contract.ts
|
|
3
|
-
const secretTargetRegistryEntries = createChannelSecretTargetRegistryEntries({
|
|
4
|
-
channelKey: "nextcloud-talk",
|
|
5
|
-
account: ["apiPassword", "botSecret"],
|
|
6
|
-
channel: ["apiPassword", "botSecret"]
|
|
7
|
-
});
|
|
8
|
-
function collectRuntimeConfigAssignments(params) {
|
|
9
|
-
const resolved = getChannelSurface(params.config, "nextcloud-talk");
|
|
10
|
-
if (!resolved) return;
|
|
11
|
-
const { channel: nextcloudTalk, surface } = resolved;
|
|
12
|
-
const inheritsField = (field) => ({ account, enabled }) => enabled && !hasOwnProperty(account, field);
|
|
13
|
-
collectConditionalChannelFieldAssignments({
|
|
14
|
-
channelKey: "nextcloud-talk",
|
|
15
|
-
field: "botSecret",
|
|
16
|
-
channel: nextcloudTalk,
|
|
17
|
-
surface,
|
|
18
|
-
defaults: params.defaults,
|
|
19
|
-
context: params.context,
|
|
20
|
-
topLevelActiveWithoutAccounts: true,
|
|
21
|
-
topLevelInheritedAccountActive: inheritsField("botSecret"),
|
|
22
|
-
accountActive: ({ enabled }) => enabled,
|
|
23
|
-
topInactiveReason: "no enabled Nextcloud Talk surface inherits this top-level botSecret.",
|
|
24
|
-
accountInactiveReason: "Nextcloud Talk account is disabled."
|
|
25
|
-
});
|
|
26
|
-
collectConditionalChannelFieldAssignments({
|
|
27
|
-
channelKey: "nextcloud-talk",
|
|
28
|
-
field: "apiPassword",
|
|
29
|
-
channel: nextcloudTalk,
|
|
30
|
-
surface,
|
|
31
|
-
defaults: params.defaults,
|
|
32
|
-
context: params.context,
|
|
33
|
-
topLevelActiveWithoutAccounts: true,
|
|
34
|
-
topLevelInheritedAccountActive: inheritsField("apiPassword"),
|
|
35
|
-
accountActive: ({ enabled }) => enabled,
|
|
36
|
-
topInactiveReason: "no enabled Nextcloud Talk surface inherits this top-level apiPassword.",
|
|
37
|
-
accountInactiveReason: "Nextcloud Talk account is disabled."
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
const channelSecrets = {
|
|
41
|
-
secretTargetRegistryEntries,
|
|
42
|
-
collectRuntimeConfigAssignments
|
|
43
|
-
};
|
|
44
|
-
//#endregion
|
|
45
|
-
export { collectRuntimeConfigAssignments as n, secretTargetRegistryEntries as r, channelSecrets as t };
|