@openclaw/nextcloud-talk 2026.7.1 → 2026.7.2-beta.2
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-BMKRTDSP.js → channel-BSnehBf6.js} +165 -246
- package/dist/channel-plugin-api.js +1 -1
- package/dist/doctor-contract-api.js +1 -1
- package/dist/doctor-contract-fNhK1wag.js +22 -0
- package/dist/{secret-contract-CsCJ-vOP.js → secret-contract-DGgvfVqm.js} +6 -47
- package/dist/secret-contract-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/openclaw.plugin.json +72 -48
- package/package.json +4 -4
- package/dist/doctor-contract-BY7I5-v9.js +0 -7
package/dist/api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as nextcloudTalkPlugin } from "./channel-
|
|
1
|
+
import { t as nextcloudTalkPlugin } from "./channel-BSnehBf6.js";
|
|
2
2
|
export { nextcloudTalkPlugin };
|
|
@@ -1,47 +1,47 @@
|
|
|
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-fNhK1wag.js";
|
|
3
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-DGgvfVqm.js";
|
|
4
4
|
import { 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 } from "openclaw/plugin-sdk/channel-policy";
|
|
7
|
+
import { createAllowlistProviderRouteAllowlistWarningCollector, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
|
|
8
8
|
import { buildWebhookChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
9
|
+
import { convertMarkdownTables, sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
10
|
+
import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
11
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
12
|
import { DEFAULT_ACCOUNT_ID, createAccountListHelpers, hasConfiguredAccountValue, normalizeAccountId, resolveAccountWithDefaultFallback, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-core";
|
|
10
13
|
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
|
|
11
|
-
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
12
14
|
import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
13
|
-
import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
14
15
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
15
16
|
import { parseStrictNonNegativeInteger, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
16
17
|
import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
17
|
-
import { createAccountStatusSink, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
18
|
+
import { createAccountStatusSink, createMessageReceiptFromOutboundResults, defineChannelMessageAdapter, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
18
19
|
import { ssrfPolicyFromPrivateNetworkOptIn, ssrfPolicyFromPrivateNetworkOptIn as ssrfPolicyFromPrivateNetworkOptIn$1 } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
19
|
-
import { readFileSync } from "node:fs";
|
|
20
20
|
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
21
21
|
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
22
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
23
|
+
import { fileExists, safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
24
24
|
import { clearAccountEntryFields } from "openclaw/plugin-sdk/channel-plugin-common";
|
|
25
25
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2 } from "openclaw/plugin-sdk/account-id";
|
|
26
|
-
import { DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, ReplyRuntimeConfigSchemaShape,
|
|
26
|
+
import { DmPolicySchema, GroupPolicySchema, MarkdownConfigSchema, ReplyRuntimeConfigSchemaShape, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
|
|
27
27
|
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";
|
|
32
31
|
import os from "node:os";
|
|
33
32
|
import path from "node:path";
|
|
34
|
-
import {
|
|
33
|
+
import { createChannelReplayGuard, migratePersistentDedupeLegacyJsonFile } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
35
34
|
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
36
35
|
import { channelIngressRoutes, resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
37
36
|
import { resolveInboundRouteEnvelopeBuilderWithRuntime } from "openclaw/plugin-sdk/inbound-envelope";
|
|
38
|
-
import { buildChannelKeyCandidates, normalizeChannelSlug, resolveChannelEntryMatchWithFallback
|
|
37
|
+
import { buildChannelKeyCandidates, normalizeChannelSlug, resolveChannelEntryMatchWithFallback } from "openclaw/plugin-sdk/channel-targets";
|
|
39
38
|
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
39
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
40
40
|
import { createServer } from "node:http";
|
|
41
41
|
import { WEBHOOK_RATE_LIMIT_DEFAULTS, createAuthRateLimiter, isRequestBodyLimitError, readRequestBodyWithLimit, requestBodyErrorToText } from "openclaw/plugin-sdk/webhook-ingress";
|
|
42
42
|
import { jsonResult, readStringParam, resolveReactionMessageId } from "openclaw/plugin-sdk/channel-actions";
|
|
43
43
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, buildOutboundBaseSessionKey, normalizeAccountId as normalizeAccountId$1 } from "openclaw/plugin-sdk/routing";
|
|
44
|
-
import { applyAccountNameToChannelSection, createSetupTranslator, createStandardChannelSetupStatus, formatDocsLink, patchScopedAccountConfig, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
|
|
44
|
+
import { applyAccountNameToChannelSection, baseUrlTextInput, createSetupTranslator, createStandardChannelSetupStatus, defineTokenCredential, formatDocsLink, patchScopedAccountConfig, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
|
|
45
45
|
import { createSetupInputPresenceValidator, createSetupTranslator as createSetupTranslator$1, mergeAllowFromEntries, promptParsedAllowFromForAccount, resolveSetupAccountId } from "openclaw/plugin-sdk/setup-runtime";
|
|
46
46
|
import { formatDocsLink as formatDocsLink$1 } from "openclaw/plugin-sdk/setup-tools";
|
|
47
47
|
//#region extensions/nextcloud-talk/src/accounts.ts
|
|
@@ -139,19 +139,16 @@ function resolveNextcloudTalkAccount(params) {
|
|
|
139
139
|
function normalizeNextcloudTalkApproverId(value) {
|
|
140
140
|
return normalizeOptionalLowercaseString(String(value).trim().replace(/^(nextcloud-talk|nc-talk|nc):/i, ""));
|
|
141
141
|
}
|
|
142
|
-
const nextcloudTalkApprovalAuth =
|
|
142
|
+
const nextcloudTalkApprovalAuth = createChannelApprovalAuth({
|
|
143
143
|
channelLabel: "Nextcloud Talk",
|
|
144
|
-
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}).config.allowFrom,
|
|
150
|
-
normalizeApprover: normalizeNextcloudTalkApproverId
|
|
151
|
-
});
|
|
144
|
+
resolveInputs: ({ cfg, accountId }) => {
|
|
145
|
+
return { allowFrom: resolveNextcloudTalkAccount({
|
|
146
|
+
cfg,
|
|
147
|
+
accountId
|
|
148
|
+
}).config.allowFrom };
|
|
152
149
|
},
|
|
153
|
-
|
|
154
|
-
});
|
|
150
|
+
normalizeApprover: normalizeNextcloudTalkApproverId
|
|
151
|
+
}).approvalAuth;
|
|
155
152
|
//#endregion
|
|
156
153
|
//#region extensions/nextcloud-talk/src/api-credentials.ts
|
|
157
154
|
function resolveNextcloudTalkApiCredentials(params) {
|
|
@@ -167,10 +164,10 @@ function resolveNextcloudTalkApiCredentials(params) {
|
|
|
167
164
|
};
|
|
168
165
|
if (!params.apiPasswordFile) return;
|
|
169
166
|
try {
|
|
170
|
-
const
|
|
171
|
-
return
|
|
167
|
+
const fileValue = tryReadSecretFileSync(params.apiPasswordFile, "Nextcloud Talk API password", { rejectHardlinks: false });
|
|
168
|
+
return fileValue ? {
|
|
172
169
|
apiUser,
|
|
173
|
-
apiPassword:
|
|
170
|
+
apiPassword: fileValue
|
|
174
171
|
} : void 0;
|
|
175
172
|
} catch {
|
|
176
173
|
return;
|
|
@@ -188,16 +185,7 @@ const BACKEND_HEADER = "x-nextcloud-talk-backend";
|
|
|
188
185
|
function verifyNextcloudTalkSignature(params) {
|
|
189
186
|
const { signature, random, body, secret } = params;
|
|
190
187
|
if (!signature || !random || !secret) return false;
|
|
191
|
-
|
|
192
|
-
const expectedBuf = Buffer.from(expected, "utf8");
|
|
193
|
-
const signatureBuf = Buffer.from(signature, "utf8");
|
|
194
|
-
const maxLen = Math.max(expectedBuf.length, signatureBuf.length);
|
|
195
|
-
const paddedExpected = Buffer.alloc(maxLen);
|
|
196
|
-
const paddedSignature = Buffer.alloc(maxLen);
|
|
197
|
-
expectedBuf.copy(paddedExpected);
|
|
198
|
-
signatureBuf.copy(paddedSignature);
|
|
199
|
-
const timingResult = timingSafeEqual(paddedExpected, paddedSignature);
|
|
200
|
-
return expectedBuf.length === signatureBuf.length && timingResult;
|
|
188
|
+
return safeEqualSecret(signature, createHmac("sha256", secret).update(random + body).digest("hex"));
|
|
201
189
|
}
|
|
202
190
|
/**
|
|
203
191
|
* Extract webhook headers from an incoming request.
|
|
@@ -372,18 +360,11 @@ const nextcloudTalkPairingTextAdapter = {
|
|
|
372
360
|
};
|
|
373
361
|
//#endregion
|
|
374
362
|
//#region extensions/nextcloud-talk/src/config-schema.ts
|
|
375
|
-
const NextcloudTalkRoomSchema = z.
|
|
376
|
-
requireMention: z.boolean().optional(),
|
|
377
|
-
tools: ToolPolicySchema,
|
|
378
|
-
skills: z.array(z.string()).optional(),
|
|
379
|
-
enabled: z.boolean().optional(),
|
|
380
|
-
allowFrom: z.array(z.string()).optional(),
|
|
381
|
-
systemPrompt: z.string().optional()
|
|
382
|
-
}).strict();
|
|
363
|
+
const NextcloudTalkRoomSchema = buildGroupEntrySchema({ allowFrom: z.array(z.string()).optional() }).omit({ toolsBySender: true });
|
|
383
364
|
const NextcloudTalkNetworkSchema = z.object({
|
|
384
365
|
/** Dangerous opt-in for self-hosted Nextcloud Talk on trusted private/internal hosts. */
|
|
385
366
|
dangerouslyAllowPrivateNetwork: z.boolean().optional() }).strict().optional();
|
|
386
|
-
const
|
|
367
|
+
const NextcloudTalkConfigSchema = buildMultiAccountChannelSchema(z.object({
|
|
387
368
|
name: z.string().optional(),
|
|
388
369
|
enabled: z.boolean().optional(),
|
|
389
370
|
markdown: MarkdownConfigSchema,
|
|
@@ -405,27 +386,17 @@ const NextcloudTalkAccountSchemaBase = z.object({
|
|
|
405
386
|
/** Network policy overrides for self-hosted Nextcloud Talk on trusted private/internal hosts. */
|
|
406
387
|
network: NextcloudTalkNetworkSchema,
|
|
407
388
|
...ReplyRuntimeConfigSchemaShape
|
|
408
|
-
}).strict()
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
accounts: z.record(z.string(), NextcloudTalkAccountSchema.optional()).optional(),
|
|
420
|
-
defaultAccount: z.string().optional()
|
|
421
|
-
}).superRefine((value, ctx) => {
|
|
422
|
-
requireChannelOpenAllowFrom({
|
|
423
|
-
channel: "nextcloud-talk",
|
|
424
|
-
policy: value.dmPolicy,
|
|
425
|
-
allowFrom: value.allowFrom,
|
|
426
|
-
ctx,
|
|
427
|
-
requireOpenAllowFrom
|
|
428
|
-
});
|
|
389
|
+
}).strict(), {
|
|
390
|
+
optionalAccount: true,
|
|
391
|
+
refine: (value, ctx) => {
|
|
392
|
+
requireChannelOpenAllowFrom({
|
|
393
|
+
channel: "nextcloud-talk",
|
|
394
|
+
policy: value.dmPolicy,
|
|
395
|
+
allowFrom: value.allowFrom,
|
|
396
|
+
ctx,
|
|
397
|
+
requireOpenAllowFrom
|
|
398
|
+
});
|
|
399
|
+
}
|
|
429
400
|
});
|
|
430
401
|
//#endregion
|
|
431
402
|
//#region extensions/nextcloud-talk/src/replay-guard.ts
|
|
@@ -434,7 +405,7 @@ const NEXTCLOUD_TALK_REPLAY_DEDUPE_NAMESPACE_PREFIX = "replay-dedupe";
|
|
|
434
405
|
const DEFAULT_REPLAY_TTL_MS = 1440 * 60 * 1e3;
|
|
435
406
|
const DEFAULT_MEMORY_MAX_SIZE = 1e3;
|
|
436
407
|
const DEFAULT_STATE_MAX_ENTRIES = 1e4;
|
|
437
|
-
function
|
|
408
|
+
function buildNextcloudTalkReplayKey(params) {
|
|
438
409
|
const roomToken = params.roomToken.trim();
|
|
439
410
|
const messageId = params.messageId.trim();
|
|
440
411
|
if (!roomToken || !messageId) return null;
|
|
@@ -446,55 +417,21 @@ function createNextcloudTalkReplayGuard(options) {
|
|
|
446
417
|
ttlMs: options.ttlMs ?? DEFAULT_REPLAY_TTL_MS,
|
|
447
418
|
memoryMaxSize: options.memoryMaxSize ?? DEFAULT_MEMORY_MAX_SIZE
|
|
448
419
|
};
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
messageId
|
|
465
|
-
});
|
|
466
|
-
if (!replayKey) return "invalid";
|
|
467
|
-
return (await dedupe.claim(replayKey, { namespace: accountId })).kind;
|
|
468
|
-
},
|
|
469
|
-
commitMessage: async ({ accountId, roomToken, messageId }) => {
|
|
470
|
-
const replayKey = buildReplayKey({
|
|
471
|
-
roomToken,
|
|
472
|
-
messageId
|
|
473
|
-
});
|
|
474
|
-
if (!replayKey) return true;
|
|
475
|
-
return await dedupe.commit(replayKey, { namespace: accountId });
|
|
476
|
-
},
|
|
477
|
-
releaseMessage: ({ accountId, roomToken, messageId, error }) => {
|
|
478
|
-
const replayKey = buildReplayKey({
|
|
479
|
-
roomToken,
|
|
480
|
-
messageId
|
|
481
|
-
});
|
|
482
|
-
if (!replayKey) return;
|
|
483
|
-
dedupe.release(replayKey, {
|
|
484
|
-
namespace: accountId,
|
|
485
|
-
error
|
|
486
|
-
});
|
|
487
|
-
},
|
|
488
|
-
shouldProcessMessage: async ({ accountId, roomToken, messageId }) => {
|
|
489
|
-
const replayKey = buildReplayKey({
|
|
490
|
-
roomToken,
|
|
491
|
-
messageId
|
|
492
|
-
});
|
|
493
|
-
if (!replayKey) return true;
|
|
494
|
-
if ((await dedupe.claim(replayKey, { namespace: accountId })).kind !== "claimed") return false;
|
|
495
|
-
return await dedupe.commit(replayKey, { namespace: accountId });
|
|
496
|
-
}
|
|
497
|
-
};
|
|
420
|
+
return createChannelReplayGuard({
|
|
421
|
+
dedupe: stateDir ? {
|
|
422
|
+
...baseOptions,
|
|
423
|
+
pluginId: NEXTCLOUD_TALK_PLUGIN_ID,
|
|
424
|
+
namespacePrefix: NEXTCLOUD_TALK_REPLAY_DEDUPE_NAMESPACE_PREFIX,
|
|
425
|
+
stateMaxEntries: options.stateMaxEntries ?? options.fileMaxEntries ?? DEFAULT_STATE_MAX_ENTRIES,
|
|
426
|
+
env: {
|
|
427
|
+
...process.env,
|
|
428
|
+
OPENCLAW_STATE_DIR: stateDir
|
|
429
|
+
},
|
|
430
|
+
onDiskError: options.onDiskError
|
|
431
|
+
} : baseOptions,
|
|
432
|
+
buildReplayKey: buildNextcloudTalkReplayKey,
|
|
433
|
+
namespace: (event) => event.accountId
|
|
434
|
+
});
|
|
498
435
|
}
|
|
499
436
|
//#endregion
|
|
500
437
|
//#region extensions/nextcloud-talk/src/doctor.ts
|
|
@@ -505,14 +442,6 @@ function sanitizeLegacyReplaySegment(value) {
|
|
|
505
442
|
if (!trimmed) return "default";
|
|
506
443
|
return trimmed.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
507
444
|
}
|
|
508
|
-
async function fileExists(filePath) {
|
|
509
|
-
try {
|
|
510
|
-
await fs.access(filePath);
|
|
511
|
-
return true;
|
|
512
|
-
} catch {
|
|
513
|
-
return false;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
445
|
async function collectNextcloudTalkBotResponseWarnings(params) {
|
|
517
446
|
const warnings = [];
|
|
518
447
|
for (const accountId of listNextcloudTalkAccountIds(params.cfg)) {
|
|
@@ -537,7 +466,7 @@ async function repairNextcloudTalkReplayDedupeState(params) {
|
|
|
537
466
|
const replayDir = path.join(stateDir, "nextcloud-talk", "replay-dedupe");
|
|
538
467
|
for (const accountId of listNextcloudTalkAccountIds(params.cfg)) {
|
|
539
468
|
const legacyPath = path.join(replayDir, `${sanitizeLegacyReplaySegment(accountId)}.json`);
|
|
540
|
-
if (!
|
|
469
|
+
if (!fileExists(legacyPath)) continue;
|
|
541
470
|
try {
|
|
542
471
|
const result = await migratePersistentDedupeLegacyJsonFile({
|
|
543
472
|
filePath: legacyPath,
|
|
@@ -592,12 +521,11 @@ function resolveNextcloudTalkAllowlistMatch(params) {
|
|
|
592
521
|
matchSource: "wildcard"
|
|
593
522
|
};
|
|
594
523
|
const senderId = normalizeNextcloudTalkAllowEntry(params.senderId);
|
|
595
|
-
|
|
524
|
+
return allowFrom.includes(senderId) ? {
|
|
596
525
|
allowed: true,
|
|
597
526
|
matchKey: senderId,
|
|
598
527
|
matchSource: "id"
|
|
599
|
-
};
|
|
600
|
-
return { allowed: false };
|
|
528
|
+
} : { allowed: false };
|
|
601
529
|
}
|
|
602
530
|
function resolveNextcloudTalkRoomMatch(params) {
|
|
603
531
|
const rooms = params.rooms ?? {};
|
|
@@ -609,12 +537,7 @@ function resolveNextcloudTalkRoomMatch(params) {
|
|
|
609
537
|
normalizeKey: normalizeChannelSlug
|
|
610
538
|
});
|
|
611
539
|
const roomConfig = match.entry;
|
|
612
|
-
const allowed =
|
|
613
|
-
outerConfigured: allowlistConfigured,
|
|
614
|
-
outerMatched: Boolean(roomConfig),
|
|
615
|
-
innerConfigured: false,
|
|
616
|
-
innerMatched: false
|
|
617
|
-
});
|
|
540
|
+
const allowed = !allowlistConfigured || Boolean(roomConfig);
|
|
618
541
|
return {
|
|
619
542
|
roomConfig,
|
|
620
543
|
wildcardConfig: match.wildcardEntry,
|
|
@@ -625,25 +548,52 @@ function resolveNextcloudTalkRoomMatch(params) {
|
|
|
625
548
|
};
|
|
626
549
|
}
|
|
627
550
|
function resolveNextcloudTalkGroupToolPolicy(params) {
|
|
628
|
-
const cfg = params.cfg;
|
|
629
551
|
const roomToken = params.groupId?.trim();
|
|
630
552
|
if (!roomToken) return;
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
|
|
553
|
+
const { tree, toolsPath } = buildNextcloudTalkRoomScope(resolveNextcloudTalkAccount({
|
|
554
|
+
cfg: params.cfg,
|
|
555
|
+
accountId: params.accountId
|
|
556
|
+
}).config.rooms, roomToken);
|
|
557
|
+
return resolveScopeToolsPolicy({
|
|
558
|
+
tree,
|
|
559
|
+
path: toolsPath
|
|
634
560
|
});
|
|
635
|
-
return match.roomConfig?.tools ?? match.wildcardConfig?.tools;
|
|
636
561
|
}
|
|
637
|
-
function
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
562
|
+
function buildNextcloudTalkRoomScope(rooms, roomToken) {
|
|
563
|
+
const { "*": defaults, ...scopes } = rooms ?? {};
|
|
564
|
+
const tree = {
|
|
565
|
+
defaults,
|
|
566
|
+
scopes
|
|
567
|
+
};
|
|
568
|
+
const exactPath = Object.hasOwn(scopes, roomToken) ? [roomToken] : [];
|
|
569
|
+
const toolsMatch = resolveChannelEntryMatchWithFallback({
|
|
570
|
+
entries: scopes,
|
|
571
|
+
keys: buildChannelKeyCandidates(roomToken),
|
|
572
|
+
normalizeKey: normalizeChannelSlug
|
|
573
|
+
});
|
|
574
|
+
return {
|
|
575
|
+
tree,
|
|
576
|
+
exactPath,
|
|
577
|
+
toolsPath: toolsMatch.matchKey ? [toolsMatch.matchKey] : []
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function resolveNextcloudTalkGroupRequireMention(params) {
|
|
581
|
+
if (!params.groupId) return true;
|
|
582
|
+
const { tree, exactPath } = buildNextcloudTalkRoomScope(resolveNextcloudTalkAccount({
|
|
583
|
+
cfg: params.cfg,
|
|
584
|
+
accountId: params.accountId
|
|
585
|
+
}).config.rooms, params.groupId);
|
|
586
|
+
return resolveScopeRequireMention({
|
|
587
|
+
tree,
|
|
588
|
+
path: exactPath
|
|
589
|
+
});
|
|
641
590
|
}
|
|
642
591
|
//#endregion
|
|
643
592
|
//#region extensions/nextcloud-talk/src/room-info.ts
|
|
644
593
|
const ROOM_CACHE_TTL_MS = 300 * 1e3;
|
|
645
594
|
const ROOM_CACHE_ERROR_TTL_MS = 30 * 1e3;
|
|
646
595
|
const ROOM_CACHE_MAX_ENTRIES = 1e3;
|
|
596
|
+
const NEXTCLOUD_TALK_ROOM_INFO_TIMEOUT_MS = 3e4;
|
|
647
597
|
const roomCache = /* @__PURE__ */ new Map();
|
|
648
598
|
function resolveRoomCacheKey(params) {
|
|
649
599
|
return `${params.accountId}:${params.roomToken}`;
|
|
@@ -695,7 +645,8 @@ async function resolveNextcloudTalkRoomKind(params) {
|
|
|
695
645
|
}
|
|
696
646
|
},
|
|
697
647
|
auditContext: "nextcloud-talk.room-info",
|
|
698
|
-
policy: ssrfPolicyFromPrivateNetworkOptIn(account.config)
|
|
648
|
+
policy: ssrfPolicyFromPrivateNetworkOptIn(account.config),
|
|
649
|
+
timeoutMs: params.timeoutMs ?? NEXTCLOUD_TALK_ROOM_INFO_TIMEOUT_MS
|
|
699
650
|
});
|
|
700
651
|
try {
|
|
701
652
|
if (!response.ok) {
|
|
@@ -730,10 +681,10 @@ function stripNextcloudTalkTargetPrefix(raw) {
|
|
|
730
681
|
const trimmed = raw.trim();
|
|
731
682
|
if (!trimmed) return;
|
|
732
683
|
let normalized = trimmed;
|
|
733
|
-
if (
|
|
734
|
-
else if (
|
|
735
|
-
else if (
|
|
736
|
-
if (
|
|
684
|
+
if (/^nextcloud-talk:/i.test(normalized)) normalized = normalized.slice(15).trim();
|
|
685
|
+
else if (/^nc-talk:/i.test(normalized)) normalized = normalized.slice(8).trim();
|
|
686
|
+
else if (/^nc:/i.test(normalized)) normalized = normalized.slice(3).trim();
|
|
687
|
+
if (/^room:/i.test(normalized)) normalized = normalized.slice(5).trim();
|
|
737
688
|
if (!normalized) return;
|
|
738
689
|
return normalized;
|
|
739
690
|
}
|
|
@@ -744,17 +695,18 @@ function normalizeNextcloudTalkMessagingTarget(raw) {
|
|
|
744
695
|
function looksLikeNextcloudTalkTargetId(raw) {
|
|
745
696
|
const trimmed = raw.trim();
|
|
746
697
|
if (!trimmed) return false;
|
|
747
|
-
if (/^(nextcloud-talk|nc-talk|nc):/i.test(trimmed)) return true;
|
|
698
|
+
if (/^(nextcloud-talk|nc-talk|nc|room):/i.test(trimmed)) return true;
|
|
748
699
|
return /^[a-z0-9]{8,}$/i.test(trimmed);
|
|
749
700
|
}
|
|
750
701
|
//#endregion
|
|
751
702
|
//#region extensions/nextcloud-talk/src/send.ts
|
|
752
703
|
const NEXTCLOUD_TALK_ERROR_SNIPPET_MAX_BYTES = 8 * 1024;
|
|
753
704
|
const NEXTCLOUD_TALK_ERROR_SNIPPET_MAX_CHARS = 200;
|
|
705
|
+
const NEXTCLOUD_TALK_SEND_TIMEOUT_MS = 3e4;
|
|
754
706
|
/** Collapses whitespace and caps an error-body prefix to a short, log-safe snippet. */
|
|
755
707
|
function collapseErrorSnippet(text) {
|
|
756
708
|
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
757
|
-
if (collapsed.length > NEXTCLOUD_TALK_ERROR_SNIPPET_MAX_CHARS) return `${collapsed
|
|
709
|
+
if (collapsed.length > NEXTCLOUD_TALK_ERROR_SNIPPET_MAX_CHARS) return `${truncateUtf16Safe(collapsed, NEXTCLOUD_TALK_ERROR_SNIPPET_MAX_CHARS)}…`;
|
|
758
710
|
return collapsed;
|
|
759
711
|
}
|
|
760
712
|
/** Reads a bounded, collapsed error-body snippet without buffering hostile responses. */
|
|
@@ -850,7 +802,8 @@ async function sendMessageNextcloudTalk(to, text, opts) {
|
|
|
850
802
|
body: bodyStr
|
|
851
803
|
},
|
|
852
804
|
auditContext: "nextcloud-talk-send",
|
|
853
|
-
policy: ssrfPolicyFromPrivateNetworkOptIn$1(account.config)
|
|
805
|
+
policy: ssrfPolicyFromPrivateNetworkOptIn$1(account.config),
|
|
806
|
+
timeoutMs: opts.timeoutMs ?? NEXTCLOUD_TALK_SEND_TIMEOUT_MS
|
|
854
807
|
});
|
|
855
808
|
try {
|
|
856
809
|
if (!response.ok) {
|
|
@@ -908,7 +861,8 @@ async function sendReactionNextcloudTalk(roomToken, messageId, reaction, opts) {
|
|
|
908
861
|
body
|
|
909
862
|
},
|
|
910
863
|
auditContext: "nextcloud-talk-reaction",
|
|
911
|
-
policy: ssrfPolicyFromPrivateNetworkOptIn$1(account.config)
|
|
864
|
+
policy: ssrfPolicyFromPrivateNetworkOptIn$1(account.config),
|
|
865
|
+
timeoutMs: opts.timeoutMs ?? NEXTCLOUD_TALK_SEND_TIMEOUT_MS
|
|
912
866
|
});
|
|
913
867
|
try {
|
|
914
868
|
if (!response.ok) {
|
|
@@ -959,7 +913,7 @@ function roomRoutes(params) {
|
|
|
959
913
|
}
|
|
960
914
|
async function deliverNextcloudTalkReply(params) {
|
|
961
915
|
const { cfg, payload, roomToken, accountId, statusSink } = params;
|
|
962
|
-
await deliverFormattedTextWithAttachments({
|
|
916
|
+
return { visibleReplySent: await deliverFormattedTextWithAttachments({
|
|
963
917
|
payload,
|
|
964
918
|
send: async ({ text, replyToId }) => {
|
|
965
919
|
await sendMessageNextcloudTalk(roomToken, text, {
|
|
@@ -969,7 +923,7 @@ async function deliverNextcloudTalkReply(params) {
|
|
|
969
923
|
});
|
|
970
924
|
statusSink?.({ lastOutboundAt: Date.now() });
|
|
971
925
|
}
|
|
972
|
-
});
|
|
926
|
+
}) };
|
|
973
927
|
}
|
|
974
928
|
async function handleNextcloudTalkInbound(params) {
|
|
975
929
|
const { message, account, config, runtime, statusSink } = params;
|
|
@@ -1002,9 +956,10 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1002
956
|
surface: CHANNEL_ID
|
|
1003
957
|
});
|
|
1004
958
|
const hasControlCommand = core.channel.text.hasControlCommand(rawBody, config);
|
|
1005
|
-
const shouldRequireMention = isGroup ?
|
|
1006
|
-
|
|
1007
|
-
|
|
959
|
+
const shouldRequireMention = isGroup ? resolveNextcloudTalkGroupRequireMention({
|
|
960
|
+
cfg: config,
|
|
961
|
+
accountId: account.accountId,
|
|
962
|
+
groupId: roomToken
|
|
1008
963
|
}) : false;
|
|
1009
964
|
const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({
|
|
1010
965
|
providerConfigPresent: (config.channels?.[CHANNEL_ID] ?? void 0) !== void 0,
|
|
@@ -1141,6 +1096,7 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1141
1096
|
body: rawBody
|
|
1142
1097
|
});
|
|
1143
1098
|
const groupSystemPrompt = normalizeOptionalString(roomConfig?.systemPrompt);
|
|
1099
|
+
const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(account.config);
|
|
1144
1100
|
const ctxPayload = core.channel.reply.finalizeInboundContext({
|
|
1145
1101
|
Body: body,
|
|
1146
1102
|
BodyForAgent: rawBody,
|
|
@@ -1176,8 +1132,12 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1176
1132
|
recordInboundSession: core.channel.session.recordInboundSession,
|
|
1177
1133
|
dispatchReplyWithBufferedBlockDispatcher: core.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
|
|
1178
1134
|
delivery: {
|
|
1135
|
+
preparePayload: (payload) => payload.text === void 0 ? payload : {
|
|
1136
|
+
...payload,
|
|
1137
|
+
text: sanitizeAssistantVisibleText(payload.text)
|
|
1138
|
+
},
|
|
1179
1139
|
deliver: async (payload) => {
|
|
1180
|
-
await deliverNextcloudTalkReply({
|
|
1140
|
+
return await deliverNextcloudTalkReply({
|
|
1181
1141
|
cfg: config,
|
|
1182
1142
|
payload,
|
|
1183
1143
|
roomToken,
|
|
@@ -1192,7 +1152,7 @@ async function handleNextcloudTalkInbound(params) {
|
|
|
1192
1152
|
replyPipeline: {},
|
|
1193
1153
|
replyOptions: {
|
|
1194
1154
|
skillFilter: roomConfig?.skills,
|
|
1195
|
-
disableBlockStreaming: typeof
|
|
1155
|
+
disableBlockStreaming: typeof blockStreamingEnabled === "boolean" ? !blockStreamingEnabled : void 0
|
|
1196
1156
|
},
|
|
1197
1157
|
record: { onRecordError: (err) => {
|
|
1198
1158
|
runtime.error?.(`nextcloud-talk: failed updating session meta: ${String(err)}`);
|
|
@@ -1242,40 +1202,12 @@ const WEBHOOK_ERRORS = {
|
|
|
1242
1202
|
payloadTooLarge: "Payload too large",
|
|
1243
1203
|
internalServerError: "Internal server error"
|
|
1244
1204
|
};
|
|
1245
|
-
var NextcloudTalkRetryableWebhookError = class extends Error {
|
|
1246
|
-
constructor(message, options) {
|
|
1247
|
-
super(message, options);
|
|
1248
|
-
this.name = "NextcloudTalkRetryableWebhookError";
|
|
1249
|
-
}
|
|
1250
|
-
};
|
|
1251
1205
|
async function processNextcloudTalkReplayGuardedMessage(params) {
|
|
1252
|
-
|
|
1206
|
+
return (await params.replayGuard.processGuarded({
|
|
1253
1207
|
accountId: params.accountId,
|
|
1254
1208
|
roomToken: params.message.roomToken,
|
|
1255
1209
|
messageId: params.message.messageId
|
|
1256
|
-
}
|
|
1257
|
-
try {
|
|
1258
|
-
await params.handleMessage();
|
|
1259
|
-
await params.replayGuard.commitMessage({
|
|
1260
|
-
accountId: params.accountId,
|
|
1261
|
-
roomToken: params.message.roomToken,
|
|
1262
|
-
messageId: params.message.messageId
|
|
1263
|
-
});
|
|
1264
|
-
return "processed";
|
|
1265
|
-
} catch (error) {
|
|
1266
|
-
if (error instanceof NextcloudTalkRetryableWebhookError) params.replayGuard.releaseMessage({
|
|
1267
|
-
accountId: params.accountId,
|
|
1268
|
-
roomToken: params.message.roomToken,
|
|
1269
|
-
messageId: params.message.messageId,
|
|
1270
|
-
error
|
|
1271
|
-
});
|
|
1272
|
-
else await params.replayGuard.commitMessage({
|
|
1273
|
-
accountId: params.accountId,
|
|
1274
|
-
roomToken: params.message.roomToken,
|
|
1275
|
-
messageId: params.message.messageId
|
|
1276
|
-
});
|
|
1277
|
-
throw error;
|
|
1278
|
-
}
|
|
1210
|
+
}, params.handleMessage, { onError: "commit" })).kind === "processed" ? "processed" : "duplicate";
|
|
1279
1211
|
}
|
|
1280
1212
|
function formatError(err) {
|
|
1281
1213
|
if (err instanceof Error) return err.message;
|
|
@@ -1651,7 +1583,7 @@ const nextcloudTalkMessageActions = {
|
|
|
1651
1583
|
if (!hasConfiguredAccount(cfg, accountId)) return null;
|
|
1652
1584
|
return { actions: ["send", "react"] };
|
|
1653
1585
|
},
|
|
1654
|
-
supportsAction: ({ action }) => action
|
|
1586
|
+
supportsAction: ({ action }) => action === "react",
|
|
1655
1587
|
handleAction: async ({ action, params, cfg, accountId, toolContext }) => {
|
|
1656
1588
|
if (action === "send") throw new Error("Send should be handled by outbound, not actions handler.");
|
|
1657
1589
|
if (action === "react") {
|
|
@@ -1938,8 +1870,10 @@ const nextcloudTalkSetupWizard = {
|
|
|
1938
1870
|
[CONFIGURE_API_FLAG]: "1"
|
|
1939
1871
|
} };
|
|
1940
1872
|
},
|
|
1941
|
-
credentials: [{
|
|
1873
|
+
credentials: [defineTokenCredential({
|
|
1942
1874
|
inputKey: "token",
|
|
1875
|
+
configKey: "botSecret",
|
|
1876
|
+
configuredFields: ["botSecret", "botSecretFile"],
|
|
1943
1877
|
providerHint: channel,
|
|
1944
1878
|
credentialLabel: t("wizard.nextcloudTalk.botSecret"),
|
|
1945
1879
|
preferredEnvVar: "NEXTCLOUD_TALK_BOT_SECRET",
|
|
@@ -1947,61 +1881,56 @@ const nextcloudTalkSetupWizard = {
|
|
|
1947
1881
|
keepPrompt: t("wizard.nextcloudTalk.botSecretKeep"),
|
|
1948
1882
|
inputPrompt: t("wizard.nextcloudTalk.botSecretInput"),
|
|
1949
1883
|
allowEnv: ({ accountId }) => accountId === DEFAULT_ACCOUNT_ID$1,
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
};
|
|
1884
|
+
resolveAccount: ({ cfg, accountId }) => resolveNextcloudTalkAccount({
|
|
1885
|
+
cfg,
|
|
1886
|
+
accountId
|
|
1887
|
+
}),
|
|
1888
|
+
accountConfigured: (account) => Boolean(account.secret && account.baseUrl),
|
|
1889
|
+
hasConfiguredValue: (account) => Boolean(hasConfiguredSecretInput(account.config.botSecret) || account.config.botSecretFile),
|
|
1890
|
+
resolvedValue: (account) => account.secret || void 0,
|
|
1891
|
+
envValue: ({ accountId }) => accountId === DEFAULT_ACCOUNT_ID$1 ? normalizeOptionalString(process.env.NEXTCLOUD_TALK_BOT_SECRET) : void 0,
|
|
1892
|
+
patchAccount: ({ cfg, accountId, patch, clearFields }) => {
|
|
1893
|
+
return setNextcloudTalkAccountConfig(clearNextcloudTalkAccountFields(cfg, accountId, clearFields), accountId, patch);
|
|
1961
1894
|
},
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
accountId: params.accountId
|
|
1966
|
-
});
|
|
1967
|
-
return setNextcloudTalkAccountConfig(clearNextcloudTalkAccountFields(params.cfg, params.accountId, ["botSecret", "botSecretFile"]), params.accountId, { baseUrl: resolvedAccount.baseUrl });
|
|
1895
|
+
useEnv: {
|
|
1896
|
+
clearFields: ["botSecret", "botSecretFile"],
|
|
1897
|
+
patch: (account) => ({ baseUrl: account.baseUrl })
|
|
1968
1898
|
},
|
|
1969
|
-
|
|
1970
|
-
}, {
|
|
1899
|
+
set: { clearFields: ["botSecret", "botSecretFile"] }
|
|
1900
|
+
}), defineTokenCredential({
|
|
1971
1901
|
inputKey: "password",
|
|
1902
|
+
configKey: "apiPassword",
|
|
1903
|
+
configuredFields: ["apiPassword", "apiPasswordFile"],
|
|
1972
1904
|
providerHint: "nextcloud-talk-api",
|
|
1973
1905
|
credentialLabel: t("wizard.nextcloudTalk.apiPassword"),
|
|
1974
1906
|
preferredEnvVar: "NEXTCLOUD_TALK_API_PASSWORD",
|
|
1975
1907
|
envPrompt: "",
|
|
1976
1908
|
keepPrompt: t("wizard.nextcloudTalk.apiPasswordKeep"),
|
|
1977
1909
|
inputPrompt: t("wizard.nextcloudTalk.apiPasswordInput"),
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
const apiPasswordConfigured = Boolean(hasConfiguredSecretInput(resolvedAccount.config.apiPassword) || resolvedAccount.config.apiPasswordFile);
|
|
1985
|
-
return {
|
|
1986
|
-
accountConfigured: Boolean(apiUser && apiPasswordConfigured),
|
|
1987
|
-
hasConfiguredValue: apiPasswordConfigured
|
|
1988
|
-
};
|
|
1989
|
-
},
|
|
1910
|
+
resolveAccount: ({ cfg, accountId }) => resolveNextcloudTalkAccount({
|
|
1911
|
+
cfg,
|
|
1912
|
+
accountId
|
|
1913
|
+
}),
|
|
1914
|
+
accountConfigured: (account) => Boolean(account.config.apiUser?.trim() && (hasConfiguredSecretInput(account.config.apiPassword) || account.config.apiPasswordFile)),
|
|
1915
|
+
hasConfiguredValue: (account) => Boolean(hasConfiguredSecretInput(account.config.apiPassword) || account.config.apiPasswordFile),
|
|
1990
1916
|
shouldPrompt: ({ credentialValues }) => credentialValues[CONFIGURE_API_FLAG] === "1",
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1917
|
+
patchAccount: ({ cfg, accountId, patch, clearFields }) => setNextcloudTalkAccountConfig(clearNextcloudTalkAccountFields(cfg, accountId, clearFields), accountId, patch),
|
|
1918
|
+
set: { clearFields: ["apiPassword", "apiPasswordFile"] }
|
|
1919
|
+
})],
|
|
1920
|
+
textInputs: [baseUrlTextInput({
|
|
1994
1921
|
inputKey: "httpUrl",
|
|
1922
|
+
configKey: "baseUrl",
|
|
1995
1923
|
message: t("wizard.nextcloudTalk.instanceUrlPrompt"),
|
|
1996
|
-
|
|
1924
|
+
resolveAccount: ({ cfg, accountId }) => resolveNextcloudTalkAccount({
|
|
1997
1925
|
cfg,
|
|
1998
1926
|
accountId
|
|
1999
|
-
})
|
|
1927
|
+
}),
|
|
1928
|
+
currentValue: (account) => account.baseUrl || void 0,
|
|
2000
1929
|
shouldPrompt: ({ currentValue }) => !currentValue,
|
|
2001
|
-
validate:
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
}, {
|
|
1930
|
+
validate: validateNextcloudTalkBaseUrl,
|
|
1931
|
+
normalize: normalizeNextcloudTalkBaseUrl,
|
|
1932
|
+
patchAccount: ({ cfg, accountId, patch }) => setNextcloudTalkAccountConfig(cfg, accountId, patch)
|
|
1933
|
+
}), {
|
|
2005
1934
|
inputKey: "userId",
|
|
2006
1935
|
message: t("wizard.nextcloudTalk.apiUserPrompt"),
|
|
2007
1936
|
currentValue: ({ cfg, accountId }) => resolveNextcloudTalkAccount({
|
|
@@ -2076,18 +2005,7 @@ const nextcloudTalkPlugin = createChatChannelPlugin({
|
|
|
2076
2005
|
approvalCapability: nextcloudTalkApprovalAuth,
|
|
2077
2006
|
doctor: nextcloudTalkDoctor,
|
|
2078
2007
|
groups: {
|
|
2079
|
-
resolveRequireMention:
|
|
2080
|
-
const rooms = resolveNextcloudTalkAccount({
|
|
2081
|
-
cfg,
|
|
2082
|
-
accountId
|
|
2083
|
-
}).config.rooms;
|
|
2084
|
-
if (!rooms || !groupId) return true;
|
|
2085
|
-
const roomConfig = rooms[groupId];
|
|
2086
|
-
if (roomConfig?.requireMention !== void 0) return roomConfig.requireMention;
|
|
2087
|
-
const wildcardConfig = rooms["*"];
|
|
2088
|
-
if (wildcardConfig?.requireMention !== void 0) return wildcardConfig.requireMention;
|
|
2089
|
-
return true;
|
|
2090
|
-
},
|
|
2008
|
+
resolveRequireMention: resolveNextcloudTalkGroupRequireMention,
|
|
2091
2009
|
resolveToolPolicy: resolveNextcloudTalkGroupToolPolicy
|
|
2092
2010
|
},
|
|
2093
2011
|
messaging: {
|
|
@@ -2155,7 +2073,8 @@ const nextcloudTalkPlugin = createChatChannelPlugin({
|
|
|
2155
2073
|
deliveryMode: "direct",
|
|
2156
2074
|
chunker: (text, limit) => getNextcloudTalkRuntime().channel.text.chunkMarkdownText(text, limit),
|
|
2157
2075
|
chunkerMode: "markdown",
|
|
2158
|
-
textChunkLimit: 4e3
|
|
2076
|
+
textChunkLimit: 4e3,
|
|
2077
|
+
sanitizeText: ({ text }) => sanitizeAssistantVisibleText(text)
|
|
2159
2078
|
},
|
|
2160
2079
|
attachedResults: {
|
|
2161
2080
|
channel: "nextcloud-talk",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as nextcloudTalkPlugin } from "./channel-
|
|
1
|
+
import { t as nextcloudTalkPlugin } from "./channel-BSnehBf6.js";
|
|
2
2
|
export { nextcloudTalkPlugin };
|
|
@@ -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-fNhK1wag.js";
|
|
2
2
|
export { legacyConfigRules, normalizeCompatibilityConfig };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createLegacyPrivateNetworkDoctorContract } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
2
|
+
import { defineChannelAliasMigration } from "openclaw/plugin-sdk/runtime-doctor";
|
|
3
|
+
//#region extensions/nextcloud-talk/src/doctor-contract.ts
|
|
4
|
+
const networkContract = createLegacyPrivateNetworkDoctorContract({ channelKey: "nextcloud-talk" });
|
|
5
|
+
const streamingAliasMigration = defineChannelAliasMigration({
|
|
6
|
+
channelId: "nextcloud-talk",
|
|
7
|
+
streaming: {
|
|
8
|
+
defaultMode: "partial",
|
|
9
|
+
deliveryOnly: true
|
|
10
|
+
},
|
|
11
|
+
accountStreamingReplacesRoot: true
|
|
12
|
+
});
|
|
13
|
+
const legacyConfigRules = [...networkContract.legacyConfigRules, ...streamingAliasMigration.legacyConfigRules];
|
|
14
|
+
function normalizeCompatibilityConfig({ cfg }) {
|
|
15
|
+
const network = networkContract.normalizeCompatibilityConfig({ cfg });
|
|
16
|
+
return streamingAliasMigration.normalizeChannelConfig({
|
|
17
|
+
cfg: network.config,
|
|
18
|
+
changes: network.changes
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { normalizeCompatibilityConfig as n, legacyConfigRules as t };
|
|
@@ -1,51 +1,10 @@
|
|
|
1
|
-
import { collectConditionalChannelFieldAssignments, getChannelSurface, hasOwnProperty } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
1
|
+
import { collectConditionalChannelFieldAssignments, createChannelSecretTargetRegistryEntries, getChannelSurface, hasOwnProperty } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
2
2
|
//#region extensions/nextcloud-talk/src/secret-contract.ts
|
|
3
|
-
const secretTargetRegistryEntries =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
pathPattern: "channels.nextcloud-talk.accounts.*.apiPassword",
|
|
9
|
-
secretShape: "secret_input",
|
|
10
|
-
expectedResolvedValue: "string",
|
|
11
|
-
includeInPlan: true,
|
|
12
|
-
includeInConfigure: true,
|
|
13
|
-
includeInAudit: true
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
id: "channels.nextcloud-talk.accounts.*.botSecret",
|
|
17
|
-
targetType: "channels.nextcloud-talk.accounts.*.botSecret",
|
|
18
|
-
configFile: "openclaw.json",
|
|
19
|
-
pathPattern: "channels.nextcloud-talk.accounts.*.botSecret",
|
|
20
|
-
secretShape: "secret_input",
|
|
21
|
-
expectedResolvedValue: "string",
|
|
22
|
-
includeInPlan: true,
|
|
23
|
-
includeInConfigure: true,
|
|
24
|
-
includeInAudit: true
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
id: "channels.nextcloud-talk.apiPassword",
|
|
28
|
-
targetType: "channels.nextcloud-talk.apiPassword",
|
|
29
|
-
configFile: "openclaw.json",
|
|
30
|
-
pathPattern: "channels.nextcloud-talk.apiPassword",
|
|
31
|
-
secretShape: "secret_input",
|
|
32
|
-
expectedResolvedValue: "string",
|
|
33
|
-
includeInPlan: true,
|
|
34
|
-
includeInConfigure: true,
|
|
35
|
-
includeInAudit: true
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
id: "channels.nextcloud-talk.botSecret",
|
|
39
|
-
targetType: "channels.nextcloud-talk.botSecret",
|
|
40
|
-
configFile: "openclaw.json",
|
|
41
|
-
pathPattern: "channels.nextcloud-talk.botSecret",
|
|
42
|
-
secretShape: "secret_input",
|
|
43
|
-
expectedResolvedValue: "string",
|
|
44
|
-
includeInPlan: true,
|
|
45
|
-
includeInConfigure: true,
|
|
46
|
-
includeInAudit: true
|
|
47
|
-
}
|
|
48
|
-
];
|
|
3
|
+
const secretTargetRegistryEntries = createChannelSecretTargetRegistryEntries({
|
|
4
|
+
channelKey: "nextcloud-talk",
|
|
5
|
+
account: ["apiPassword", "botSecret"],
|
|
6
|
+
channel: ["apiPassword", "botSecret"]
|
|
7
|
+
});
|
|
49
8
|
function collectRuntimeConfigAssignments(params) {
|
|
50
9
|
const resolved = getChannelSurface(params.config, "nextcloud-talk");
|
|
51
10
|
if (!resolved) return;
|
|
@@ -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-DGgvfVqm.js";
|
|
2
2
|
export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/nextcloud-talk",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.2-beta.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/nextcloud-talk",
|
|
9
|
-
"version": "2026.7.
|
|
9
|
+
"version": "2026.7.2-beta.2",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"zod": "4.4.3"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"openclaw": ">=2026.7.
|
|
14
|
+
"openclaw": ">=2026.7.2-beta.2"
|
|
15
15
|
},
|
|
16
16
|
"peerDependenciesMeta": {
|
|
17
17
|
"openclaw": {
|
package/openclaw.plugin.json
CHANGED
|
@@ -366,33 +366,45 @@
|
|
|
366
366
|
"exclusiveMinimum": 0,
|
|
367
367
|
"maximum": 9007199254740991
|
|
368
368
|
},
|
|
369
|
-
"
|
|
370
|
-
"type": "string",
|
|
371
|
-
"enum": [
|
|
372
|
-
"length",
|
|
373
|
-
"newline"
|
|
374
|
-
]
|
|
375
|
-
},
|
|
376
|
-
"blockStreaming": {
|
|
377
|
-
"type": "boolean"
|
|
378
|
-
},
|
|
379
|
-
"blockStreamingCoalesce": {
|
|
369
|
+
"streaming": {
|
|
380
370
|
"type": "object",
|
|
381
371
|
"properties": {
|
|
382
|
-
"
|
|
383
|
-
"type": "
|
|
384
|
-
"
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
"type": "integer",
|
|
389
|
-
"exclusiveMinimum": 0,
|
|
390
|
-
"maximum": 9007199254740991
|
|
372
|
+
"chunkMode": {
|
|
373
|
+
"type": "string",
|
|
374
|
+
"enum": [
|
|
375
|
+
"length",
|
|
376
|
+
"newline"
|
|
377
|
+
]
|
|
391
378
|
},
|
|
392
|
-
"
|
|
393
|
-
"type": "
|
|
394
|
-
"
|
|
395
|
-
|
|
379
|
+
"block": {
|
|
380
|
+
"type": "object",
|
|
381
|
+
"properties": {
|
|
382
|
+
"enabled": {
|
|
383
|
+
"type": "boolean"
|
|
384
|
+
},
|
|
385
|
+
"coalesce": {
|
|
386
|
+
"type": "object",
|
|
387
|
+
"properties": {
|
|
388
|
+
"minChars": {
|
|
389
|
+
"type": "integer",
|
|
390
|
+
"exclusiveMinimum": 0,
|
|
391
|
+
"maximum": 9007199254740991
|
|
392
|
+
},
|
|
393
|
+
"maxChars": {
|
|
394
|
+
"type": "integer",
|
|
395
|
+
"exclusiveMinimum": 0,
|
|
396
|
+
"maximum": 9007199254740991
|
|
397
|
+
},
|
|
398
|
+
"idleMs": {
|
|
399
|
+
"type": "integer",
|
|
400
|
+
"minimum": 0,
|
|
401
|
+
"maximum": 9007199254740991
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
"additionalProperties": false
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
"additionalProperties": false
|
|
396
408
|
}
|
|
397
409
|
},
|
|
398
410
|
"additionalProperties": false
|
|
@@ -752,33 +764,45 @@
|
|
|
752
764
|
"exclusiveMinimum": 0,
|
|
753
765
|
"maximum": 9007199254740991
|
|
754
766
|
},
|
|
755
|
-
"
|
|
756
|
-
"type": "string",
|
|
757
|
-
"enum": [
|
|
758
|
-
"length",
|
|
759
|
-
"newline"
|
|
760
|
-
]
|
|
761
|
-
},
|
|
762
|
-
"blockStreaming": {
|
|
763
|
-
"type": "boolean"
|
|
764
|
-
},
|
|
765
|
-
"blockStreamingCoalesce": {
|
|
767
|
+
"streaming": {
|
|
766
768
|
"type": "object",
|
|
767
769
|
"properties": {
|
|
768
|
-
"
|
|
769
|
-
"type": "
|
|
770
|
-
"
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
"type": "integer",
|
|
775
|
-
"exclusiveMinimum": 0,
|
|
776
|
-
"maximum": 9007199254740991
|
|
770
|
+
"chunkMode": {
|
|
771
|
+
"type": "string",
|
|
772
|
+
"enum": [
|
|
773
|
+
"length",
|
|
774
|
+
"newline"
|
|
775
|
+
]
|
|
777
776
|
},
|
|
778
|
-
"
|
|
779
|
-
"type": "
|
|
780
|
-
"
|
|
781
|
-
|
|
777
|
+
"block": {
|
|
778
|
+
"type": "object",
|
|
779
|
+
"properties": {
|
|
780
|
+
"enabled": {
|
|
781
|
+
"type": "boolean"
|
|
782
|
+
},
|
|
783
|
+
"coalesce": {
|
|
784
|
+
"type": "object",
|
|
785
|
+
"properties": {
|
|
786
|
+
"minChars": {
|
|
787
|
+
"type": "integer",
|
|
788
|
+
"exclusiveMinimum": 0,
|
|
789
|
+
"maximum": 9007199254740991
|
|
790
|
+
},
|
|
791
|
+
"maxChars": {
|
|
792
|
+
"type": "integer",
|
|
793
|
+
"exclusiveMinimum": 0,
|
|
794
|
+
"maximum": 9007199254740991
|
|
795
|
+
},
|
|
796
|
+
"idleMs": {
|
|
797
|
+
"type": "integer",
|
|
798
|
+
"minimum": 0,
|
|
799
|
+
"maximum": 9007199254740991
|
|
800
|
+
}
|
|
801
|
+
},
|
|
802
|
+
"additionalProperties": false
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
"additionalProperties": false
|
|
782
806
|
}
|
|
783
807
|
},
|
|
784
808
|
"additionalProperties": false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/nextcloud-talk",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.2-beta.2",
|
|
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.7.
|
|
11
|
+
"openclaw": ">=2026.7.2-beta.2"
|
|
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.7.
|
|
43
|
+
"pluginApi": ">=2026.7.2-beta.2"
|
|
44
44
|
},
|
|
45
45
|
"build": {
|
|
46
|
-
"openclawVersion": "2026.7.
|
|
46
|
+
"openclawVersion": "2026.7.2-beta.2"
|
|
47
47
|
},
|
|
48
48
|
"release": {
|
|
49
49
|
"publishToClawHub": true,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { createLegacyPrivateNetworkDoctorContract } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
2
|
-
//#region extensions/nextcloud-talk/src/doctor-contract.ts
|
|
3
|
-
const contract = createLegacyPrivateNetworkDoctorContract({ channelKey: "nextcloud-talk" });
|
|
4
|
-
const legacyConfigRules = contract.legacyConfigRules;
|
|
5
|
-
const normalizeCompatibilityConfig = contract.normalizeCompatibilityConfig;
|
|
6
|
-
//#endregion
|
|
7
|
-
export { normalizeCompatibilityConfig as n, legacyConfigRules as t };
|