@openclaw/synology-chat 2026.5.7 → 2026.5.9-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,3 +1,3 @@
1
- import { n as setSynologyRuntime, t as synologyChatPlugin } from "./channel-BYl2GyR_.js";
1
+ import { n as setSynologyRuntime, t as synologyChatPlugin } from "./channel-eRS88UFd.js";
2
2
  import { t as collectSynologyChatSecurityAuditFindings } from "./security-audit-Zu_nkF2x.js";
3
3
  export { collectSynologyChatSecurityAuditFindings, setSynologyRuntime, synologyChatPlugin };
@@ -1,21 +1,22 @@
1
- import { i as resolveAccount, n as synologyChatSetupWizard, r as listAccountIds, t as synologyChatSetupAdapter } from "./setup-surface-BTp4n9pb.js";
1
+ import { i as resolveAccount, n as synologyChatSetupWizard, r as listAccountIds, t as synologyChatSetupAdapter } from "./setup-surface-daP5yN4Y.js";
2
2
  import { t as collectSynologyChatSecurityAuditFindings } from "./security-audit-Zu_nkF2x.js";
3
3
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
4
4
  import { createHybridChannelConfigAdapter, createScopedDmSecurityResolver } from "openclaw/plugin-sdk/channel-config-helpers";
5
5
  import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
6
6
  import { waitUntilAbort } from "openclaw/plugin-sdk/channel-lifecycle";
7
+ import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
7
8
  import { composeWarningCollectors, createConditionalWarningCollector, projectAccountConfigWarningCollector, projectAccountWarningCollector } from "openclaw/plugin-sdk/channel-policy";
8
- import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
9
9
  import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
10
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
10
11
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/account-resolution";
11
12
  import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
12
13
  import * as http from "node:http";
13
14
  import * as https from "node:https";
14
15
  import { safeParseJsonWithSchema, safeParseWithSchema } from "openclaw/plugin-sdk/extension-shared";
16
+ import { sleep } from "openclaw/plugin-sdk/runtime-env";
15
17
  import { formatErrorMessage, resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
16
- import { z } from "zod";
18
+ import { z } from "openclaw/plugin-sdk/zod";
17
19
  import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
18
- import { z as z$1 } from "openclaw/plugin-sdk/zod";
19
20
  import { beginWebhookRequestPipelineOrReject, createFixedWindowRateLimiter, createWebhookInFlightLimiter, isRequestBodyLimitError, readRequestBodyWithLimit, registerPluginHttpRoute, requestBodyErrorToText } from "openclaw/plugin-sdk/webhook-ingress";
20
21
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
21
22
  import { buildAgentSessionKey } from "openclaw/plugin-sdk/routing";
@@ -44,9 +45,6 @@ const synologyChatApprovalAuth = createResolvedApproverActionAuthAdapter({
44
45
  */
45
46
  const MIN_SEND_INTERVAL_MS = 500;
46
47
  let lastSendTime = 0;
47
- function normalizeLowercaseStringOrEmpty$2(value) {
48
- return typeof value === "string" ? value.trim().toLowerCase() : "";
49
- }
50
48
  const ChatUserSchema = z.object({
51
49
  user_id: z.number(),
52
50
  username: z.string().optional(),
@@ -179,10 +177,10 @@ async function assertSafeWebhookFileUrl(fileUrl) {
179
177
  */
180
178
  async function resolveLegacyWebhookNameToChatUserId(params) {
181
179
  const users = await fetchChatUsers(params.incomingUrl, params.allowInsecureSsl, params.log);
182
- const lower = normalizeLowercaseStringOrEmpty$2(params.mutableWebhookUsername);
183
- const byNickname = users.find((u) => normalizeLowercaseStringOrEmpty$2(u.nickname) === lower);
180
+ const lower = normalizeLowercaseStringOrEmpty(params.mutableWebhookUsername);
181
+ const byNickname = users.find((u) => normalizeLowercaseStringOrEmpty(u.nickname) === lower);
184
182
  if (byNickname) return byNickname.user_id;
185
- const byUsername = users.find((u) => normalizeLowercaseStringOrEmpty$2(u.username) === lower);
183
+ const byUsername = users.find((u) => normalizeLowercaseStringOrEmpty(u.username) === lower);
186
184
  if (byUsername) return byUsername.user_id;
187
185
  }
188
186
  function buildWebhookBody(payload, userId) {
@@ -230,14 +228,11 @@ function doPost(url, body, allowInsecureSsl = false) {
230
228
  req.end();
231
229
  });
232
230
  }
233
- function sleep(ms) {
234
- return new Promise((resolve) => setTimeout(resolve, ms));
235
- }
236
231
  //#endregion
237
232
  //#region extensions/synology-chat/src/config-schema.ts
238
- const SynologyChatChannelConfigSchema = buildChannelConfigSchema(z$1.object({
239
- dangerouslyAllowNameMatching: z$1.boolean().optional(),
240
- dangerouslyAllowInheritedWebhookPath: z$1.boolean().optional()
233
+ const SynologyChatChannelConfigSchema = buildChannelConfigSchema(z.object({
234
+ dangerouslyAllowNameMatching: z.boolean().optional(),
235
+ dangerouslyAllowInheritedWebhookPath: z.boolean().optional()
241
236
  }).passthrough());
242
237
  //#endregion
243
238
  //#region extensions/synology-chat/src/runtime.ts
@@ -288,8 +283,8 @@ function resolveSynologyChatInboundRoute(params) {
288
283
  }
289
284
  async function deliverSynologyChatReply(params) {
290
285
  const text = params.payload.text ?? params.payload.body;
291
- if (!text) return;
292
- await sendMessage(params.account.incomingUrl, text, params.sendUserId, params.account.allowInsecureSsl);
286
+ if (!text) return { visibleReplySent: false };
287
+ return { visibleReplySent: await sendMessage(params.account.incomingUrl, text, params.sendUserId, params.account.allowInsecureSsl) };
293
288
  }
294
289
  async function dispatchSynologyChatInboundTurn(params) {
295
290
  const currentCfg = getSynologyRuntime().config.current();
@@ -364,13 +359,16 @@ async function dispatchSynologyChatInboundTurn(params) {
364
359
  ctxPayload: msgCtx,
365
360
  recordInboundSession: resolved.rt.channel.session.recordInboundSession,
366
361
  dispatchReplyWithBufferedBlockDispatcher: resolved.rt.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
367
- delivery: { deliver: async (payload) => {
368
- await deliverSynologyChatReply({
369
- account: params.account,
370
- sendUserId,
371
- payload
372
- });
373
- } },
362
+ delivery: {
363
+ durable: () => ({ to: sendUserId }),
364
+ deliver: async (payload) => {
365
+ return await deliverSynologyChatReply({
366
+ account: params.account,
367
+ sendUserId,
368
+ payload
369
+ });
370
+ }
371
+ },
374
372
  dispatcherOptions: { onReplyStart: () => {
375
373
  params.log?.info?.(`Agent reply started for ${params.msg.from}`);
376
374
  } },
@@ -476,9 +474,6 @@ var RateLimiter = class {
476
474
  };
477
475
  //#endregion
478
476
  //#region extensions/synology-chat/src/webhook-handler.ts
479
- function normalizeLowercaseStringOrEmpty$1(value) {
480
- return typeof value === "string" ? value.trim().toLowerCase() : "";
481
- }
482
477
  const rateLimiters = /* @__PURE__ */ new Map();
483
478
  const invalidTokenRateLimiters = /* @__PURE__ */ new Map();
484
479
  const webhookInFlightLimiter = createWebhookInFlightLimiter();
@@ -642,7 +637,7 @@ function extractTokenFromHeaders(req) {
642
637
  * - text <- text | message | content
643
638
  */
644
639
  function parsePayload(req, body) {
645
- const contentType = normalizeLowercaseStringOrEmpty$1(req.headers["content-type"]);
640
+ const contentType = normalizeLowercaseStringOrEmpty(req.headers["content-type"]);
646
641
  let bodyFields = {};
647
642
  if (contentType.includes("application/json")) bodyFields = parseJsonBody(body);
648
643
  else if (contentType.includes("application/x-www-form-urlencoded")) bodyFields = parseFormBody(body);
@@ -1016,9 +1011,6 @@ function registerSynologyWebhookRoute(params) {
1016
1011
  * Implements the ChannelPlugin interface following the LINE pattern.
1017
1012
  */
1018
1013
  const CHANNEL_ID = "synology-chat";
1019
- function normalizeLowercaseStringOrEmpty(value) {
1020
- return typeof value === "string" ? value.trim().toLowerCase() : "";
1021
- }
1022
1014
  const resolveSynologyChatDmPolicy = createScopedDmSecurityResolver({
1023
1015
  channelKey: CHANNEL_ID,
1024
1016
  resolvePolicy: (account) => account.dmPolicy,
@@ -1061,6 +1053,53 @@ function requireIncomingUrl(account) {
1061
1053
  if (!account.incomingUrl) throw new Error("Synology Chat incoming URL not configured");
1062
1054
  return account.incomingUrl;
1063
1055
  }
1056
+ function createSynologyChatSendResult(params) {
1057
+ return {
1058
+ channel: CHANNEL_ID,
1059
+ messageId: params.messageId,
1060
+ chatId: params.chatId,
1061
+ receipt: createMessageReceiptFromOutboundResults({
1062
+ results: [{
1063
+ channel: CHANNEL_ID,
1064
+ messageId: params.messageId,
1065
+ chatId: params.chatId,
1066
+ conversationId: params.chatId
1067
+ }],
1068
+ threadId: params.chatId,
1069
+ kind: params.kind
1070
+ })
1071
+ };
1072
+ }
1073
+ async function sendSynologyChatText(ctx) {
1074
+ const account = resolveOutboundAccount(ctx.cfg ?? {}, ctx.accountId);
1075
+ if (!await sendMessage(requireIncomingUrl(account), ctx.text, ctx.to, account.allowInsecureSsl)) throw new Error("Failed to send message to Synology Chat");
1076
+ return createSynologyChatSendResult({
1077
+ messageId: `sc-${Date.now()}`,
1078
+ chatId: ctx.to,
1079
+ kind: "text"
1080
+ });
1081
+ }
1082
+ async function sendSynologyChatMedia(ctx) {
1083
+ const account = resolveOutboundAccount(ctx.cfg ?? {}, ctx.accountId);
1084
+ if (!await sendFileUrl(requireIncomingUrl(account), ctx.mediaUrl, ctx.to, account.allowInsecureSsl)) throw new Error("Failed to send media to Synology Chat");
1085
+ return createSynologyChatSendResult({
1086
+ messageId: `sc-${Date.now()}`,
1087
+ chatId: ctx.to,
1088
+ kind: "media"
1089
+ });
1090
+ }
1091
+ const synologyChatMessageAdapter = defineChannelMessageAdapter({
1092
+ id: CHANNEL_ID,
1093
+ durableFinal: { capabilities: {
1094
+ text: true,
1095
+ media: true,
1096
+ messageSendingHooks: true
1097
+ } },
1098
+ send: {
1099
+ text: async (ctx) => await sendSynologyChatText(ctx),
1100
+ media: async (ctx) => await sendSynologyChatMedia(ctx)
1101
+ }
1102
+ });
1064
1103
  function createSynologyChatPlugin() {
1065
1104
  return createChatChannelPlugin({
1066
1105
  base: {
@@ -1160,7 +1199,8 @@ function createSynologyChatPlugin() {
1160
1199
  "- Use line breaks to separate sections",
1161
1200
  "- Use numbered or bulleted lists for clarity",
1162
1201
  "- Wrap URLs with `<URL|label>` for user-friendly links"
1163
- ] }
1202
+ ] },
1203
+ message: synologyChatMessageAdapter
1164
1204
  },
1165
1205
  pairing: { text: {
1166
1206
  idLabel: "synologyChatUserId",
@@ -1180,22 +1220,12 @@ function createSynologyChatPlugin() {
1180
1220
  outbound: {
1181
1221
  deliveryMode: "gateway",
1182
1222
  textChunkLimit: 2e3,
1183
- sendText: async ({ to, text, accountId, cfg }) => {
1184
- const account = resolveOutboundAccount(cfg ?? {}, accountId);
1185
- if (!await sendMessage(requireIncomingUrl(account), text, to, account.allowInsecureSsl)) throw new Error("Failed to send message to Synology Chat");
1186
- return attachChannelToResult(CHANNEL_ID, {
1187
- messageId: `sc-${Date.now()}`,
1188
- chatId: to
1189
- });
1190
- },
1191
- sendMedia: async ({ to, mediaUrl, accountId, cfg }) => {
1192
- const account = resolveOutboundAccount(cfg ?? {}, accountId);
1193
- const incomingUrl = requireIncomingUrl(account);
1194
- if (!mediaUrl) throw new Error("No media URL provided");
1195
- if (!await sendFileUrl(incomingUrl, mediaUrl, to, account.allowInsecureSsl)) throw new Error("Failed to send media to Synology Chat");
1196
- return attachChannelToResult(CHANNEL_ID, {
1197
- messageId: `sc-${Date.now()}`,
1198
- chatId: to
1223
+ sendText: sendSynologyChatText,
1224
+ sendMedia: async (ctx) => {
1225
+ if (!ctx.mediaUrl) throw new Error("Synology Chat media send requires mediaUrl");
1226
+ return await sendSynologyChatMedia({
1227
+ ...ctx,
1228
+ mediaUrl: ctx.mediaUrl
1199
1229
  });
1200
1230
  }
1201
1231
  }
@@ -1,2 +1,2 @@
1
- import { t as synologyChatPlugin } from "./channel-BYl2GyR_.js";
1
+ import { t as synologyChatPlugin } from "./channel-eRS88UFd.js";
2
2
  export { synologyChatPlugin };
package/dist/setup-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as synologyChatSetupWizard, t as synologyChatSetupAdapter } from "./setup-surface-BTp4n9pb.js";
1
+ import { n as synologyChatSetupWizard, t as synologyChatSetupAdapter } from "./setup-surface-daP5yN4Y.js";
2
2
  export { synologyChatSetupAdapter, synologyChatSetupWizard };
@@ -1,3 +1,4 @@
1
+ import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
1
2
  import { DEFAULT_ACCOUNT_ID, listCombinedAccountIds, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
2
3
  import { resolveDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
3
4
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createAllowFromSection, createStandardChannelSetupStatus, formatDocsLink, mergeAllowFromEntries, normalizeAccountId, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
@@ -114,10 +115,6 @@ const SYNOLOGY_ALLOW_FROM_HELP_LINES = [
114
115
  "Multiple entries: comma-separated.",
115
116
  `Docs: ${formatDocsLink("/channels/synology-chat", "channels/synology-chat")}`
116
117
  ];
117
- function normalizeOptionalString(value) {
118
- if (typeof value !== "string") return;
119
- return value.trim() || void 0;
120
- }
121
118
  function getChannelConfig(cfg) {
122
119
  return cfg.channels?.[channel] ?? {};
123
120
  }
package/package.json CHANGED
@@ -1,15 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/synology-chat",
3
- "version": "2026.5.7",
3
+ "version": "2026.5.9-beta.1",
4
4
  "description": "Synology Chat channel plugin for OpenClaw",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/openclaw/openclaw"
8
8
  },
9
9
  "type": "module",
10
- "dependencies": {
11
- "zod": "^4.4.3"
12
- },
13
10
  "devDependencies": {
14
11
  "@openclaw/plugin-sdk": "workspace:*"
15
12
  },
@@ -33,10 +30,10 @@
33
30
  "minHostVersion": ">=2026.4.10"
34
31
  },
35
32
  "compat": {
36
- "pluginApi": ">=2026.5.7"
33
+ "pluginApi": ">=2026.5.9-beta.1"
37
34
  },
38
35
  "build": {
39
- "openclawVersion": "2026.5.7"
36
+ "openclawVersion": "2026.5.9-beta.1"
40
37
  },
41
38
  "release": {
42
39
  "publishToClawHub": true,
@@ -52,7 +49,7 @@
52
49
  "openclaw.plugin.json"
53
50
  ],
54
51
  "peerDependencies": {
55
- "openclaw": ">=2026.5.7"
52
+ "openclaw": ">=2026.5.9-beta.1"
56
53
  },
57
54
  "peerDependenciesMeta": {
58
55
  "openclaw": {