@openclaw/signal 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.
Files changed (33) hide show
  1. package/dist/{accounts-DrStJjiK.js → accounts-DO4HMqaK.js} +2 -2
  2. package/dist/api.js +9 -9
  3. package/dist/{runtime-JPrGlV7h.js → approval-auth-BY6uuApE.js} +2 -9
  4. package/dist/{approval-handler.runtime-C-XYgcxE.js → approval-handler.runtime-Co6EVTqY.js} +4 -4
  5. package/dist/{approval-reactions-DtL47gSK.js → approval-reactions-cs3Ox9Ow.js} +67 -189
  6. package/dist/channel-config-api.js +1 -1
  7. package/dist/channel-plugin-api.js +1 -1
  8. package/dist/{channel-CmbWaYlm.js → channel-zIopSllO.js} +64 -99
  9. package/dist/{channel.runtime-BRDaqr5-.js → channel.runtime-CpDBElG8.js} +3 -3
  10. package/dist/{client-BazXTH7O.js → client-CvnR8_XA.js} +31 -34
  11. package/dist/{client-adapter-mXGeEHQJ.js → client-adapter-D9SNPaNx.js} +13 -6
  12. package/dist/{config-schema-Y3dFGwdL.js → config-schema-CkMIK7zp.js} +4 -5
  13. package/dist/contract-api.js +1 -1
  14. package/dist/{doctor-contract-api-Dcte-QYB.js → doctor-contract-api-Dnp5eQQJ.js} +19 -22
  15. package/dist/doctor-contract-api.js +1 -1
  16. package/dist/{install-signal-cli-qJF7iC4h.js → install-signal-cli-Cpiyk0k1.js} +8 -5
  17. package/dist/{monitor-BvIzkrYW.js → monitor-BLFIkpH0.js} +305 -142
  18. package/dist/{probe-Br9abaGp.js → probe-B0E_cXQw.js} +23 -7
  19. package/dist/question-reactions-BaatFqJ7.js +80 -0
  20. package/dist/{reaction-runtime-api-BjeDyKcz.js → reaction-runtime-api-YGD6hhDh.js} +10 -21
  21. package/dist/reaction-runtime-api.js +1 -1
  22. package/dist/rpc-context-dXSy4NtF.js +15 -0
  23. package/dist/runtime-api.js +8 -9
  24. package/dist/{send-B_Ks4MZr.js → send-BNl5folj.js} +14 -23
  25. package/dist/{send.runtime-CbuXycZT.js → send.runtime-DnMoKk27.js} +1 -1
  26. package/dist/{transport-detection-Da06-LOL.js → transport-detection-D_M79R5f.js} +60 -148
  27. package/dist/{transport-detection.runtime-D_190UUL.js → transport-detection.runtime-DKuuIHvf.js} +1 -1
  28. package/dist/{transport-probes.runtime-CV6Qs0Og.js → transport-probes.runtime-BsJdp0oI.js} +1 -1
  29. package/openclaw.plugin.json +4 -1
  30. package/package.json +5 -5
  31. package/dist/approval-resolver-B3m0D2R0.js +0 -16
  32. package/dist/question-reactions-Ds48FtEu.js +0 -109
  33. package/dist/rpc-context-CZUoE0-A.js +0 -17
@@ -1,14 +1,14 @@
1
1
  import { a as isSignalManagedNativeConnectionUrlForBind, i as assignSignalManagedNativePort, l as normalizeSignalTransportHost, o as isValidSignalManagedNativePort, r as allocateSignalManagedNativePort, s as resolveLocalSignalTransportPort, u as normalizeSignalTransportUrl } from "./transport-policy-DxvSMHp9.js";
2
- import { a as resolveSignalAccountConfig, i as resolveSignalAccount, n as listSignalAccountIds, o as resolveSignalReplyToMode, r as resolveDefaultSignalAccountId, s as resolveSignalTransport } from "./accounts-DrStJjiK.js";
2
+ import { a as resolveSignalAccountConfig, i as resolveSignalAccount, n as listSignalAccountIds, o as resolveSignalReplyToMode, r as resolveDefaultSignalAccountId, s as resolveSignalTransport } from "./accounts-DO4HMqaK.js";
3
3
  import { c as resolveSignalSender, d as normalizeSignalMessagingTarget, l as looksLikeUuid, o as resolveSignalPeerId, s as resolveSignalRecipient, u as looksLikeSignalTargetId } from "./identity-BQ4OzXXk.js";
4
- import { a as listSignalAliasDirectoryEntries, o as resolveSignalTarget } from "./runtime-JPrGlV7h.js";
5
- import { a as signalMessageActions, d as shouldSuppressLocalSignalExecApprovalPrompt, f as signalApprovalCapability, i as materializeSignalPresentationFallback, l as markdownToSignalTextChunks, o as resolveSignalReactionLevel, r as resolveSignalReplyContextWithPersistence, s as formatSignalMediaText, t as detectSignalTransport } from "./transport-detection-Da06-LOL.js";
6
- import { i as migrateLegacySignalTransportConfig, n as normalizeCompatibilityConfig, r as clearLegacySignalTransportFieldsForAccount } from "./doctor-contract-api-Dcte-QYB.js";
7
- import { t as SignalChannelConfigSchema } from "./config-schema-Y3dFGwdL.js";
4
+ import { i as resolveSignalTarget, r as listSignalAliasDirectoryEntries } from "./approval-auth-BY6uuApE.js";
5
+ import { c as resolveSignalReactionLevel, d as markdownToSignalTextChunks, l as formatSignalMediaText, m as signalApprovalCapability, o as materializeSignalPresentationFallback, p as shouldSuppressLocalSignalExecApprovalPrompt, r as resolveSignalReplyContextWithPersistence, s as signalMessageActions, t as detectSignalTransport } from "./transport-detection-D_M79R5f.js";
6
+ import { i as migrateLegacySignalTransportConfig, n as normalizeCompatibilityConfig, r as clearLegacySignalTransportFieldsForAccount } from "./doctor-contract-api-Dnp5eQQJ.js";
7
+ import { t as SignalChannelConfigSchema } from "./config-schema-CkMIK7zp.js";
8
8
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
9
9
  import { buildDmGroupAccountAllowlistAdapter } from "openclaw/plugin-sdk/allowlist-config-edit";
10
10
  import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
11
- import { createReplyToFanout, defineChannelMessageAdapter, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
11
+ import { createAccountStatusSink, createReplyToFanout, defineChannelMessageAdapter, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
12
12
  import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
13
13
  import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
14
14
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
@@ -213,8 +213,8 @@ async function probeSignalTransport(params) {
213
213
  accountId: params.accountId,
214
214
  overrides: managedTransportOptions(params.transport)
215
215
  }) : params.transport);
216
- if (resolved.kind === "container") return (params.probeContainer ?? (await import("./transport-probes.runtime-CV6Qs0Og.js")).containerCheck)(resolved.baseUrl, timeoutMs, params.account);
217
- return (params.probeNative ?? (await import("./transport-probes.runtime-CV6Qs0Og.js")).nativeCheck)(resolved.baseUrl, timeoutMs);
216
+ if (resolved.kind === "container") return (params.probeContainer ?? (await import("./transport-probes.runtime-BsJdp0oI.js")).containerCheck)(resolved.baseUrl, timeoutMs, params.account);
217
+ return (params.probeNative ?? (await import("./transport-probes.runtime-BsJdp0oI.js")).nativeCheck)(resolved.baseUrl, timeoutMs);
218
218
  }
219
219
  function writeSignalAccountTransport(params) {
220
220
  const transport = normalizeTransport(params.transport);
@@ -324,6 +324,7 @@ function parseSignalAllowFromEntries(raw) {
324
324
  });
325
325
  }
326
326
  function buildSignalSetupPatch(input) {
327
+ const account = normalizeSignalAccountInput(input.signalNumber);
327
328
  const transport = input.httpUrl ? {
328
329
  kind: input.signalTransport ?? "external-native",
329
330
  url: normalizeSignalTransportUrl(input.httpUrl)
@@ -334,7 +335,7 @@ function buildSignalSetupPatch(input) {
334
335
  ...input.httpPort ? { httpPort: Number(input.httpPort) } : {}
335
336
  } : void 0;
336
337
  return {
337
- ...input.signalNumber ? { account: input.signalNumber } : {},
338
+ ...account ? { account } : {},
338
339
  ...transport ? { transport } : {}
339
340
  };
340
341
  }
@@ -463,6 +464,7 @@ const signalSetupAdapterBase = createPatchedAccountSetupAdapter({
463
464
  } catch {
464
465
  return "Signal --http-host must be a hostname or IP address.";
465
466
  }
467
+ if (input.signalNumber !== void 0 && !normalizeSignalAccountInput(input.signalNumber)) return INVALID_SIGNAL_ACCOUNT_ERROR;
466
468
  if (input.signalTransport === "container" && !normalizeSignalAccountInput(input.signalNumber) && !normalizeSignalAccountInput(resolveSignalSetupAccount({
467
469
  cfg,
468
470
  accountId
@@ -568,7 +570,7 @@ function createSignalSetupWizardProxy(loadWizard) {
568
570
  const signalDoctor = {
569
571
  normalizeCompatibilityConfig,
570
572
  cleanStaleConfig: async ({ cfg }) => {
571
- const { detectSignalTransport } = await import("./transport-detection.runtime-D_190UUL.js");
573
+ const { detectSignalTransport } = await import("./transport-detection.runtime-DKuuIHvf.js");
572
574
  return await migrateLegacySignalTransportConfig({
573
575
  cfg,
574
576
  detect: detectSignalTransport
@@ -579,7 +581,7 @@ const signalDoctor = {
579
581
  //#region extensions/signal/src/shared.ts
580
582
  const SIGNAL_CHANNEL = "signal";
581
583
  async function loadSignalChannelRuntime() {
582
- return await import("./channel.runtime-BRDaqr5-.js");
584
+ return await import("./channel.runtime-CpDBElG8.js");
583
585
  }
584
586
  const signalSetupWizard = createSignalSetupWizardProxy(async () => (await loadSignalChannelRuntime()).signalSetupWizard);
585
587
  const signalConfigAdapter = {
@@ -625,6 +627,7 @@ const signalSecurityAdapter = createRestrictSendersChannelSecurity({
625
627
  groupPolicyPath: "channels.signal.groupPolicy",
626
628
  groupAllowFromPath: "channels.signal.groupAllowFrom",
627
629
  mentionGated: false,
630
+ findingTitle: "Signal security warning",
628
631
  policyPathSuffix: "dmPolicy",
629
632
  normalizeDmEntry: (raw) => normalizeE164(raw.replace(/^signal:/i, "").trim())
630
633
  });
@@ -663,10 +666,10 @@ function createSignalPluginBase(params) {
663
666
  }
664
667
  //#endregion
665
668
  //#region extensions/signal/src/channel.ts
666
- const loadSignalMonitorModule = createLazyRuntimeModule(() => import("./monitor-BvIzkrYW.js").then((n) => n.n));
667
- const loadSignalProbeModule = createLazyRuntimeModule(() => import("./probe-Br9abaGp.js").then((n) => n.n));
668
- const loadSignalSendRuntime = createLazyRuntimeModule(() => import("./send.runtime-CbuXycZT.js"));
669
- const loadSignalApprovalReactionsModule = createLazyRuntimeModule(() => import("./approval-reactions-DtL47gSK.js").then((n) => n.r));
669
+ const loadSignalMonitorModule = createLazyRuntimeModule(() => import("./monitor-BLFIkpH0.js").then((n) => n.n));
670
+ const loadSignalProbeModule = createLazyRuntimeModule(() => import("./probe-B0E_cXQw.js").then((n) => n.n));
671
+ const loadSignalSendRuntime = createLazyRuntimeModule(() => import("./send.runtime-DnMoKk27.js"));
672
+ const loadSignalApprovalReactionsModule = createLazyRuntimeModule(() => import("./approval-reactions-cs3Ox9Ow.js").then((n) => n.n));
670
673
  async function resolveSignalSendContext(params) {
671
674
  return {
672
675
  send: resolveOutboundSendDep(params.deps, "signal") ?? (await loadSignalSendRuntime()).sendMessageSignal,
@@ -685,21 +688,29 @@ function resolveSignalSendTarget(params) {
685
688
  })?.to ?? params.to.trim();
686
689
  }
687
690
  async function sendSignalOutbound(params) {
688
- const { send, maxBytes } = await resolveSignalSendContext(params);
689
- const to = resolveSignalSendTarget(params);
691
+ const accountId = params.accountId ?? void 0;
692
+ const { send, maxBytes } = await resolveSignalSendContext({
693
+ ...params,
694
+ accountId
695
+ });
696
+ const to = resolveSignalSendTarget({
697
+ ...params,
698
+ accountId
699
+ });
690
700
  const replyOptions = await resolveSignalReplyOptions({
691
701
  cfg: params.cfg,
692
702
  to,
693
- accountId: params.accountId,
703
+ accountId,
694
704
  replyToId: params.replyToId
695
705
  });
696
706
  return await send(to, params.text, {
697
707
  cfg: params.cfg,
698
708
  ...params.mediaUrl ? { mediaUrl: params.mediaUrl } : {},
709
+ ...params.mediaAccess ? { mediaAccess: params.mediaAccess } : {},
699
710
  ...params.mediaLocalRoots?.length ? { mediaLocalRoots: params.mediaLocalRoots } : {},
700
711
  ...params.mediaReadFile ? { mediaReadFile: params.mediaReadFile } : {},
701
712
  maxBytes,
702
- accountId: params.accountId ?? void 0,
713
+ accountId,
703
714
  ...replyOptions
704
715
  });
705
716
  }
@@ -729,9 +740,7 @@ function inferSignalTargetChatType(rawTo) {
729
740
  if (!to) return;
730
741
  if (/^signal:/i.test(to)) to = to.replace(/^signal:/i, "").trim();
731
742
  if (!to) return;
732
- const lower = normalizeLowercaseStringOrEmpty(to);
733
- if (lower.startsWith("group:")) return "group";
734
- if (lower.startsWith("username:") || lower.startsWith("u:")) return "direct";
743
+ if (normalizeLowercaseStringOrEmpty(to).startsWith("group:")) return "group";
735
744
  return "direct";
736
745
  }
737
746
  function attachSignalVisibleText(result, visibleText) {
@@ -740,6 +749,7 @@ function attachSignalVisibleText(result, visibleText) {
740
749
  ...result,
741
750
  meta: {
742
751
  ...meta,
752
+ visibleText,
743
753
  signalVisibleText: visibleText
744
754
  }
745
755
  };
@@ -748,28 +758,14 @@ const signalMessageAdapter = defineChannelMessageAdapter({
748
758
  id: "signal",
749
759
  durableFinal: { capabilities: {
750
760
  text: true,
751
- media: true
761
+ media: true,
762
+ payload: true,
763
+ replyTo: true,
764
+ messageSendingHooks: true
752
765
  } },
753
766
  send: {
754
- text: async (ctx) => await sendSignalOutbound({
755
- cfg: ctx.cfg,
756
- to: ctx.to,
757
- text: ctx.text,
758
- accountId: ctx.accountId ?? void 0,
759
- deps: ctx.deps,
760
- replyToId: ctx.replyToId ?? void 0
761
- }),
762
- media: async (ctx) => await sendSignalOutbound({
763
- cfg: ctx.cfg,
764
- to: ctx.to,
765
- text: ctx.text,
766
- mediaUrl: ctx.mediaUrl,
767
- mediaLocalRoots: ctx.mediaLocalRoots,
768
- mediaReadFile: ctx.mediaReadFile,
769
- accountId: ctx.accountId ?? void 0,
770
- deps: ctx.deps,
771
- replyToId: ctx.replyToId ?? void 0
772
- })
767
+ text: sendSignalOutbound,
768
+ media: sendSignalOutbound
773
769
  }
774
770
  });
775
771
  function buildSignalBaseSessionKey(params) {
@@ -819,14 +815,15 @@ async function sendFormattedSignalText(ctx) {
819
815
  text: ctx.text,
820
816
  styles: []
821
817
  }];
818
+ const effectiveReplyToMode = ctx.replyToMode ?? resolveSignalReplyToMode({
819
+ cfg: ctx.cfg,
820
+ accountId: ctx.accountId,
821
+ chatType: inferSignalTargetChatType(to)
822
+ });
822
823
  const nextReplyToId = createReplyToFanout({
823
824
  replyToId: ctx.replyToId,
824
825
  replyToIdSource: ctx.replyToIdSource,
825
- replyToMode: ctx.replyToMode ?? resolveSignalReplyToMode({
826
- cfg: ctx.cfg,
827
- accountId: ctx.accountId,
828
- chatType: inferSignalTargetChatType(to)
829
- })
826
+ replyToMode: effectiveReplyToMode
830
827
  });
831
828
  const results = [];
832
829
  for (const chunk of chunks) {
@@ -881,6 +878,7 @@ async function sendFormattedSignalMedia(ctx) {
881
878
  return attachChannelToResult("signal", attachSignalVisibleText(await send(to, formatted.text, {
882
879
  cfg: ctx.cfg,
883
880
  mediaUrl: ctx.mediaUrl,
881
+ ...ctx.mediaAccess ? { mediaAccess: ctx.mediaAccess } : {},
884
882
  mediaLocalRoots: ctx.mediaLocalRoots,
885
883
  ...ctx.mediaReadFile ? { mediaReadFile: ctx.mediaReadFile } : {},
886
884
  maxBytes,
@@ -898,7 +896,7 @@ async function registerDeliveredSignalApprovalPayloadForReactions(params) {
898
896
  const targetAuthor = normalizeOptionalString(account.config.account);
899
897
  const targetAuthorUuid = normalizeOptionalString(account.config.accountUuid);
900
898
  if (!targetAuthor && !targetAuthorUuid) return;
901
- const { registerSignalQuestionReactionTargetForDeliveredPayload } = await import("./question-reactions-Ds48FtEu.js").then((n) => n.n);
899
+ const { registerSignalQuestionReactionTargetForDeliveredPayload } = await import("./question-reactions-BaatFqJ7.js").then((n) => n.n);
902
900
  registerSignalQuestionReactionTargetForDeliveredPayload({
903
901
  cfg: params.cfg,
904
902
  target: {
@@ -1047,9 +1045,13 @@ const signalPlugin = createChatChannelPlugin({
1047
1045
  lastProbeAt: snapshot.lastProbeAt ?? null
1048
1046
  }),
1049
1047
  probeAccount: async ({ account, timeoutMs }) => {
1050
- const baseUrl = account.baseUrl;
1051
- const { probeSignal } = await loadSignalProbeModule();
1052
- return await probeSignal(baseUrl, timeoutMs, { transportKind: account.transport.kind });
1048
+ const { probeSignalAccount } = await loadSignalProbeModule();
1049
+ return await probeSignalAccount({
1050
+ baseUrl: account.baseUrl,
1051
+ timeoutMs,
1052
+ transportKind: account.transport.kind,
1053
+ account: account.config.account
1054
+ });
1053
1055
  },
1054
1056
  formatCapabilitiesProbe: ({ probe }) => probe?.version ? [{ text: `Signal daemon: ${probe.version}` }] : [],
1055
1057
  resolveAccountSnapshot: ({ account }) => ({
@@ -1062,10 +1064,11 @@ const signalPlugin = createChatChannelPlugin({
1062
1064
  }),
1063
1065
  gateway: { startAccount: async (ctx) => {
1064
1066
  const account = ctx.account;
1065
- ctx.setStatus({
1067
+ const statusSink = createAccountStatusSink({
1066
1068
  accountId: account.accountId,
1067
- baseUrl: account.baseUrl
1069
+ setStatus: ctx.setStatus
1068
1070
  });
1071
+ statusSink({ baseUrl: account.baseUrl });
1069
1072
  ctx.log?.info(`[${account.accountId}] starting provider (${account.baseUrl})`);
1070
1073
  const { monitorSignalProvider } = await loadSignalMonitorModule();
1071
1074
  return await monitorSignalProvider({
@@ -1074,7 +1077,8 @@ const signalPlugin = createChatChannelPlugin({
1074
1077
  runtime: ctx.runtime,
1075
1078
  channelRuntime: ctx.channelRuntime,
1076
1079
  abortSignal: ctx.abortSignal,
1077
- mediaMaxMb: account.config.mediaMaxMb
1080
+ mediaMaxMb: account.config.mediaMaxMb,
1081
+ statusSink
1078
1082
  });
1079
1083
  } },
1080
1084
  message: signalMessageAdapter
@@ -1153,54 +1157,15 @@ const signalPlugin = createChatChannelPlugin({
1153
1157
  payload,
1154
1158
  hint
1155
1159
  }),
1156
- afterDeliverPayload: async (params) => await registerDeliveredSignalApprovalPayloadForReactions(params),
1157
- renderPresentation: async (params) => await renderSignalApprovalPayloadForReactions(params),
1158
- sendFormattedText: async ({ cfg, to, text, accountId, deps, replyToId, replyToIdSource, replyToMode, abortSignal, onDeliveryResult }) => await sendFormattedSignalText({
1159
- cfg,
1160
- to,
1161
- text,
1162
- accountId,
1163
- deps,
1164
- replyToId,
1165
- replyToIdSource,
1166
- replyToMode,
1167
- abortSignal,
1168
- onDeliveryResult
1169
- }),
1170
- sendFormattedMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, mediaReadFile, accountId, deps, replyToId, abortSignal }) => await sendFormattedSignalMedia({
1171
- cfg,
1172
- to,
1173
- text,
1174
- mediaUrl,
1175
- mediaLocalRoots,
1176
- mediaReadFile,
1177
- accountId,
1178
- deps,
1179
- replyToId,
1180
- abortSignal
1181
- })
1160
+ afterDeliverPayload: registerDeliveredSignalApprovalPayloadForReactions,
1161
+ renderPresentation: renderSignalApprovalPayloadForReactions,
1162
+ sendFormattedText: sendFormattedSignalText,
1163
+ sendFormattedMedia: sendFormattedSignalMedia
1182
1164
  },
1183
1165
  attachedResults: {
1184
1166
  channel: "signal",
1185
- sendText: async ({ cfg, to, text, accountId, deps, replyToId }) => await sendSignalOutbound({
1186
- cfg,
1187
- to,
1188
- text,
1189
- accountId: accountId ?? void 0,
1190
- deps,
1191
- replyToId
1192
- }),
1193
- sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, mediaReadFile, accountId, deps, replyToId }) => await sendSignalOutbound({
1194
- cfg,
1195
- to,
1196
- text,
1197
- mediaUrl,
1198
- mediaLocalRoots,
1199
- mediaReadFile,
1200
- accountId: accountId ?? void 0,
1201
- deps,
1202
- replyToId
1203
- })
1167
+ sendText: sendSignalOutbound,
1168
+ sendMedia: sendSignalOutbound
1204
1169
  }
1205
1170
  }
1206
1171
  });
@@ -1,6 +1,6 @@
1
- import { i as resolveSignalAccount, n as listSignalAccountIds } from "./accounts-DrStJjiK.js";
2
- import { c as signalNumberTextInput, i as createSignalCliPathTextInput, o as signalCompletionNote, s as signalDmPolicy } from "./channel-CmbWaYlm.js";
3
- import { i as installSignalCli } from "./install-signal-cli-qJF7iC4h.js";
1
+ import { i as resolveSignalAccount, n as listSignalAccountIds } from "./accounts-DO4HMqaK.js";
2
+ import { c as signalNumberTextInput, i as createSignalCliPathTextInput, o as signalCompletionNote, s as signalDmPolicy } from "./channel-zIopSllO.js";
3
+ import { i as installSignalCli } from "./install-signal-cli-Cpiyk0k1.js";
4
4
  import { detectBinary } from "openclaw/plugin-sdk/setup-tools";
5
5
  import { createDetectedBinaryStatus, createSetupTranslator, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
6
6
  //#region extensions/signal/src/setup-surface.ts
@@ -1,7 +1,6 @@
1
- import { detectMime, parseMediaContentLength } from "openclaw/plugin-sdk/media-runtime";
1
+ import { detectMime, extractOriginalFilename, parseMediaContentLength } from "openclaw/plugin-sdk/media-runtime";
2
2
  import { parseStrictNonNegativeInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
3
- import nodePath from "node:path";
4
- import { formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
3
+ import { coerceErrorMessage, formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
5
4
  import { resolveFetch } from "openclaw/plugin-sdk/fetch-runtime";
6
5
  import { readResponseTextPrefix, readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
7
6
  import { readRegularFile } from "openclaw/plugin-sdk/security-runtime";
@@ -24,6 +23,7 @@ const SIGNAL_REST_ERROR_RESPONSE_MAX_BYTES = 16 * 1024;
24
23
  const SIGNAL_REST_SUCCESS_RESPONSE_MAX_BYTES = 16 * 1024 * 1024;
25
24
  const WS_MAX_PAYLOAD = 1024 * 1024;
26
25
  const WS_HANDSHAKE_MS = 3e4;
26
+ const WS_SHUTDOWN_DRAIN_TIMEOUT_MS = 1500;
27
27
  const DEFAULT_SIGNAL_CONTAINER_MAX_ATTACHMENT_BYTES = 8 * 1024 * 1024;
28
28
  const CONTAINER_TEXT_STYLE_MARKERS = {
29
29
  BOLD: "**",
@@ -148,7 +148,7 @@ async function containerCheck(baseUrl, timeoutMs = DEFAULT_TIMEOUT_MS$1, account
148
148
  return {
149
149
  ok: false,
150
150
  status: null,
151
- error: err instanceof Error ? err.message : String(err)
151
+ error: coerceErrorMessage(err)
152
152
  };
153
153
  } finally {
154
154
  await releaseUnreadResponseBody(res);
@@ -181,7 +181,7 @@ function containerReceiveCheck(normalizedBaseUrl, account, timeoutMs) {
181
181
  settle({
182
182
  ok: false,
183
183
  status: null,
184
- error: err instanceof Error ? err.message : String(err)
184
+ error: coerceErrorMessage(err)
185
185
  });
186
186
  return;
187
187
  }
@@ -205,7 +205,7 @@ function containerReceiveCheck(normalizedBaseUrl, account, timeoutMs) {
205
205
  settle({
206
206
  ok: false,
207
207
  status: null,
208
- error: err instanceof Error ? err.message : String(err)
208
+ error: coerceErrorMessage(err)
209
209
  });
210
210
  });
211
211
  ws.once("close", (code, reason) => {
@@ -297,7 +297,12 @@ async function streamContainerEvents(params) {
297
297
  let settled = false;
298
298
  let eventChain = Promise.resolve();
299
299
  let abortHandler;
300
+ let shutdownDrainTimer;
300
301
  const cleanup = () => {
302
+ if (shutdownDrainTimer) {
303
+ clearTimeout(shutdownDrainTimer);
304
+ shutdownDrainTimer = void 0;
305
+ }
301
306
  if (abortHandler) {
302
307
  params.abortSignal?.removeEventListener("abort", abortHandler);
303
308
  abortHandler = void 0;
@@ -321,12 +326,13 @@ async function streamContainerEvents(params) {
321
326
  handshakeTimeout: WS_HANDSHAKE_MS
322
327
  });
323
328
  } catch (err) {
324
- logError(`[signal-ws] failed to create WebSocket: ${err instanceof Error ? err.message : String(err)}`);
329
+ logError(`[signal-ws] failed to create WebSocket: ${coerceErrorMessage(err)}`);
325
330
  reject(toErrorObject(err, "Non-Error rejection"));
326
331
  return;
327
332
  }
328
333
  ws.on("open", () => {
329
334
  log("[signal-ws] connected");
335
+ params.onStreamOpen?.();
330
336
  });
331
337
  ws.on("message", (data) => {
332
338
  if (settled) return;
@@ -338,17 +344,17 @@ async function streamContainerEvents(params) {
338
344
  await params.onEvent(envelope);
339
345
  });
340
346
  eventChain.catch((err) => {
341
- logError(`[signal-ws] receive handler failed: ${err instanceof Error ? err.message : String(err)}`);
347
+ logError(`[signal-ws] receive handler failed: ${coerceErrorMessage(err)}`);
342
348
  rejectOnce(err);
343
349
  ws.close();
344
350
  });
345
351
  }
346
352
  } catch (err) {
347
- logError(`[signal-ws] parse error: ${err instanceof Error ? err.message : String(err)}`);
353
+ logError(`[signal-ws] parse error: ${coerceErrorMessage(err)}`);
348
354
  }
349
355
  });
350
356
  ws.on("error", (err) => {
351
- logError(`[signal-ws] error: ${err instanceof Error ? err.message : String(err)}`);
357
+ logError(`[signal-ws] error: ${coerceErrorMessage(err)}`);
352
358
  });
353
359
  ws.on("close", (code, reason) => {
354
360
  const reasonStr = reason?.toString() || "no reason";
@@ -364,10 +370,16 @@ async function streamContainerEvents(params) {
364
370
  if (params.abortSignal) {
365
371
  abortHandler = () => {
366
372
  log("[signal-ws] aborted, closing connection");
373
+ shutdownDrainTimer = setTimeout(() => {
374
+ logError("[signal-ws] shutdown timed out draining accepted receive events; messages may be lost");
375
+ ws.terminate();
376
+ resolveOnce();
377
+ }, WS_SHUTDOWN_DRAIN_TIMEOUT_MS);
378
+ shutdownDrainTimer.unref?.();
367
379
  ws.close();
368
- resolveOnce();
369
380
  };
370
381
  params.abortSignal.addEventListener("abort", abortHandler, { once: true });
382
+ if (params.abortSignal.aborted) abortHandler();
371
383
  }
372
384
  });
373
385
  }
@@ -388,7 +400,7 @@ async function filesToBase64DataUris(filePaths, maxAttachmentBytes) {
388
400
  buffer,
389
401
  filePath
390
402
  }) ?? "application/octet-stream";
391
- const filename = nodePath.basename(filePath);
403
+ const filename = extractOriginalFilename(filePath).replace(/[,;#]/g, "_");
392
404
  const b64 = buffer.toString("base64");
393
405
  results.push(`data:${mime};filename=${filename};base64,${b64}`);
394
406
  }
@@ -492,7 +504,7 @@ async function containerSendReceipt(params) {
492
504
  return true;
493
505
  }
494
506
  /**
495
- * Send a reaction to a message via bbernhard container REST API.
507
+ * Add or remove a message reaction via the bbernhard container REST API.
496
508
  */
497
509
  async function containerSendReaction(params) {
498
510
  const payload = {
@@ -505,23 +517,7 @@ async function containerSendReaction(params) {
505
517
  return await containerRestRequest(`/v1/reactions/${encodeURIComponent(params.account)}`, {
506
518
  baseUrl: params.baseUrl,
507
519
  timeoutMs: params.timeoutMs
508
- }, "POST", payload) ?? {};
509
- }
510
- /**
511
- * Remove a reaction from a message via bbernhard container REST API.
512
- */
513
- async function containerRemoveReaction(params) {
514
- const payload = {
515
- recipient: params.recipient,
516
- reaction: params.emoji,
517
- target_author: params.targetAuthor,
518
- timestamp: params.targetTimestamp
519
- };
520
- if (params.groupId) payload.group_id = params.groupId;
521
- return await containerRestRequest(`/v1/reactions/${encodeURIComponent(params.account)}`, {
522
- baseUrl: params.baseUrl,
523
- timeoutMs: params.timeoutMs
524
- }, "DELETE", payload) ?? {};
520
+ }, params.remove ? "DELETE" : "POST", payload) ?? {};
525
521
  }
526
522
  /**
527
523
  * Strip the "uuid:" prefix that native signal-cli accepts but the container API rejects.
@@ -605,7 +601,7 @@ async function containerRpcRequest(method, params, opts) {
605
601
  const groupId = p.groupIds?.[0] ?? void 0;
606
602
  const formattedGroupId = groupId ? formatGroupIdForContainer(groupId) : void 0;
607
603
  const effectiveRecipient = formattedGroupId || recipient || "";
608
- const reactionParams = {
604
+ return await containerSendReaction({
609
605
  baseUrl: opts.baseUrl,
610
606
  account: p.account ?? "",
611
607
  recipient: effectiveRecipient,
@@ -613,9 +609,9 @@ async function containerRpcRequest(method, params, opts) {
613
609
  targetAuthor: stripUuidPrefix(p.targetAuthor ?? recipient),
614
610
  targetTimestamp: p.targetTimestamp,
615
611
  groupId: formattedGroupId,
616
- timeoutMs: opts.timeoutMs
617
- };
618
- return await (p.remove ? containerRemoveReaction : containerSendReaction)(reactionParams);
612
+ timeoutMs: opts.timeoutMs,
613
+ remove: Boolean(p.remove)
614
+ });
619
615
  }
620
616
  case "getAttachment": {
621
617
  const attachmentId = p.id;
@@ -864,6 +860,7 @@ async function streamSignalEvents(params) {
864
860
  const url = resolveSignalEndpointUrl(params.baseUrl, "/api/v1/events");
865
861
  if (params.account) url.searchParams.set("account", params.account);
866
862
  const { response, cleanup } = await openSignalEventStream(url, params.abortSignal, params.timeoutMs ?? DEFAULT_TIMEOUT_MS);
863
+ params.onStreamOpen?.();
867
864
  const decoder = new TextDecoder();
868
865
  let buffer = "";
869
866
  let bufferedBytes = 0;
@@ -1,9 +1,14 @@
1
- import { a as containerRpcRequest, i as containerCheck, n as signalRpcRequest$1, o as streamContainerEvents, r as streamSignalEvents$1, t as signalCheck$1 } from "./client-BazXTH7O.js";
1
+ import { a as containerRpcRequest, i as containerCheck, n as signalRpcRequest$1, o as streamContainerEvents, r as streamSignalEvents$1, t as signalCheck$1 } from "./client-CvnR8_XA.js";
2
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
2
3
  //#region extensions/signal/src/client-adapter.ts
4
+ /**
5
+ * Signal client adapter - unified interface for both native signal-cli and bbernhard container.
6
+ *
7
+ * This adapter provides a single API that routes to the concrete account transport.
8
+ * Exports mirror client.ts names so consumers
9
+ * only need to change their import path.
10
+ */
3
11
  const DEFAULT_TIMEOUT_MS = 1e4;
4
- function formatErrorMessage(error) {
5
- return error instanceof Error ? error.message : String(error);
6
- }
7
12
  function usesContainer(kind) {
8
13
  return kind === "container";
9
14
  }
@@ -19,7 +24,7 @@ async function signalRpcRequest(method, params, opts) {
19
24
  */
20
25
  async function signalCheck(baseUrl, timeoutMs = DEFAULT_TIMEOUT_MS, options = {}) {
21
26
  try {
22
- return usesContainer(options.transportKind) ? await containerCheck(baseUrl, timeoutMs) : await signalCheck$1(baseUrl, timeoutMs);
27
+ return usesContainer(options.transportKind) ? await containerCheck(baseUrl, timeoutMs, options.account) : await signalCheck$1(baseUrl, timeoutMs);
23
28
  } catch (error) {
24
29
  return {
25
30
  ok: false,
@@ -42,6 +47,7 @@ async function streamSignalEvents(params) {
42
47
  event: "receive",
43
48
  data: JSON.stringify(event)
44
49
  }),
50
+ onStreamOpen: params.onStreamOpen,
45
51
  logger: params.logger
46
52
  });
47
53
  return streamSignalEvents$1({
@@ -49,7 +55,8 @@ async function streamSignalEvents(params) {
49
55
  account: params.account,
50
56
  abortSignal: params.abortSignal,
51
57
  timeoutMs: params.timeoutMs,
52
- onEvent: (event) => params.onEvent(event)
58
+ onEvent: (event) => params.onEvent(event),
59
+ onStreamOpen: params.onStreamOpen
53
60
  });
54
61
  }
55
62
  //#endregion
@@ -1,7 +1,6 @@
1
1
  import { createChannelConfigUiHints } from "openclaw/plugin-sdk/channel-core";
2
- import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
2
+ import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId, resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
4
- import { isRecord as isRecord$1 } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
5
4
  import { ChannelDeliveryStreamingConfigSchema, ChannelSendReadReceiptsSchema, ExecutableTokenSchema, ReplyToModeSchema, buildChannelConfigSchema, buildChannelReactionShape, buildCommonChannelAccountShape, buildGroupEntrySchema, requireAllowlistAllowFrom, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
6
5
  import { z } from "zod";
7
6
  //#region extensions/signal/src/config-ui-hints.ts
@@ -62,11 +61,11 @@ const SIGNAL_RETIRED_TRANSPORT_KEYS = [
62
61
  ];
63
62
  const SignalTransportUrlSchema = z.string().url().regex(/^[Hh][Tt][Tt][Pp][Ss]?:\/\/(?![^/?#]*@)/, "Expected http:// or https:// URL without embedded credentials");
64
63
  function projectSignalConfigForUpdateValidation(value) {
65
- if (process.env.OPENCLAW_UPDATE_IN_PROGRESS !== "1" || !isRecord$1(value)) return value;
64
+ if (process.env.OPENCLAW_UPDATE_IN_PROGRESS !== "1" || !isRecord(value)) return value;
66
65
  const next = { ...value };
67
66
  for (const key of SIGNAL_RETIRED_TRANSPORT_KEYS) delete next[key];
68
- if (isRecord$1(value.accounts)) next.accounts = Object.fromEntries(Object.entries(value.accounts).map(([accountId, account]) => {
69
- if (!isRecord$1(account)) return [accountId, account];
67
+ if (isRecord(value.accounts)) next.accounts = Object.fromEntries(Object.entries(value.accounts).map(([accountId, account]) => {
68
+ if (!isRecord(account)) return [accountId, account];
70
69
  const nextAccount = { ...account };
71
70
  for (const key of SIGNAL_RETIRED_TRANSPORT_KEYS) delete nextAccount[key];
72
71
  return [accountId, nextAccount];
@@ -1,3 +1,3 @@
1
1
  import { d as normalizeSignalMessagingTarget, i as isSignalSenderAllowed, u as looksLikeSignalTargetId } from "./identity-BQ4OzXXk.js";
2
- import { a as installSignalCliFromRelease, i as installSignalCli, n as downloadToFile, o as looksLikeArchive, r as extractSignalCliArchive, s as pickAsset, t as MAX_SIGNAL_CLI_EXTRACTED_BYTES } from "./install-signal-cli-qJF7iC4h.js";
2
+ import { a as installSignalCliFromRelease, i as installSignalCli, n as downloadToFile, o as looksLikeArchive, r as extractSignalCliArchive, s as pickAsset, t as MAX_SIGNAL_CLI_EXTRACTED_BYTES } from "./install-signal-cli-Cpiyk0k1.js";
3
3
  export { MAX_SIGNAL_CLI_EXTRACTED_BYTES, downloadToFile, extractSignalCliArchive, installSignalCli, installSignalCliFromRelease, isSignalSenderAllowed, looksLikeArchive, looksLikeSignalTargetId, normalizeSignalMessagingTarget, pickAsset };