@openclaw/signal 0.0.0 → 2026.6.11
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/README.md +12 -2
- package/dist/accounts-hOCHbEhX.js +40 -0
- package/dist/api.js +16 -0
- package/dist/approval-handler.runtime-CeJI1wow.js +158 -0
- package/dist/approval-resolver-BR0MioAA.js +15 -0
- package/dist/channel-LmY2UpOt.js +619 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-entry.js +18 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-B0-YpkC4.js +65 -0
- package/dist/config-api-KS-qhQvD.js +2 -0
- package/dist/config-schema-BiojLEsX.js +27 -0
- package/dist/contract-api.js +3 -0
- package/dist/identity-B6O4k8xg.js +130 -0
- package/dist/index.js +18 -0
- package/dist/install-signal-cli-CXgTF3de.js +243 -0
- package/dist/message-actions-Bue0g2Kc.js +548 -0
- package/dist/monitor-CUhIKHJo.js +1404 -0
- package/dist/probe-BQ_Izoya.js +48 -0
- package/dist/reaction-runtime-api-C_PQ45D9.js +1074 -0
- package/dist/reaction-runtime-api.js +2 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/runtime-api.js +18 -0
- package/dist/secret-contract-api.js +5 -0
- package/dist/send-CBlFUkY_.js +599 -0
- package/dist/send.runtime-CfaZd8X4.js +2 -0
- package/dist/setup-entry.js +11 -0
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +22 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +528 -0
- package/node_modules/ws/lib/receiver.js +760 -0
- package/node_modules/ws/lib/sender.js +607 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +562 -0
- package/node_modules/ws/lib/websocket.js +1407 -0
- package/node_modules/ws/package.json +70 -0
- package/node_modules/ws/wrapper.mjs +21 -0
- package/npm-shrinkwrap.json +36 -0
- package/openclaw.plugin.json +771 -0
- package/package.json +77 -7
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./accounts-hOCHbEhX.js";
|
|
2
|
+
import { d as normalizeSignalMessagingTarget, u as looksLikeSignalTargetId } from "./identity-B6O4k8xg.js";
|
|
3
|
+
import { n as resolveSignalReactionLevel, t as signalMessageActions } from "./message-actions-Bue0g2Kc.js";
|
|
4
|
+
import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-C_PQ45D9.js";
|
|
5
|
+
import { n as buildChannelConfigSchema, t as SignalConfigSchema } from "./config-api-KS-qhQvD.js";
|
|
6
|
+
import { r as installSignalCli } from "./install-signal-cli-CXgTF3de.js";
|
|
7
|
+
import { t as monitorSignalProvider } from "./monitor-CUhIKHJo.js";
|
|
8
|
+
import { t as sendMessageSignal, u as setSignalRuntime } from "./send-CBlFUkY_.js";
|
|
9
|
+
import { t as probeSignal } from "./probe-BQ_Izoya.js";
|
|
10
|
+
import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
|
|
11
|
+
import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
|
|
12
|
+
import { buildBaseAccountStatusSnapshot, buildBaseChannelStatusSummary, collectStatusIssuesFromLastError, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
13
|
+
import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
14
|
+
import { DEFAULT_ACCOUNT_ID, applyAccountNameToChannelSection, deleteAccountFromConfigSection, emptyPluginConfigSchema, formatPairingApproveHint, getChatChannelMeta, migrateBaseNameToDefaultAccount, normalizeAccountId, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
|
|
15
|
+
import { detectBinary, formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
|
16
|
+
import { chunkText } from "openclaw/plugin-sdk/reply-runtime";
|
|
17
|
+
import { resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
18
|
+
export { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, SignalConfigSchema, applyAccountNameToChannelSection, buildBaseAccountStatusSnapshot, buildBaseChannelStatusSummary, buildChannelConfigSchema, chunkText, collectStatusIssuesFromLastError, createDefaultChannelRuntimeState, deleteAccountFromConfigSection, detectBinary, emptyPluginConfigSchema, formatCliCommand, formatDocsLink, formatPairingApproveHint, getChatChannelMeta, installSignalCli, listEnabledSignalAccounts, listSignalAccountIds, looksLikeSignalTargetId, migrateBaseNameToDefaultAccount, monitorSignalProvider, normalizeAccountId, normalizeE164, normalizeSignalMessagingTarget, probeSignal, removeReactionSignal, resolveAllowlistProviderRuntimeGroupPolicy, resolveChannelMediaMaxBytes, resolveDefaultGroupPolicy, resolveDefaultSignalAccountId, resolveSignalAccount, resolveSignalReactionLevel, sendMessageSignal, sendReactionSignal, setAccountEnabledInConfigSection, setSignalRuntime, signalMessageActions };
|
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
import { i as resolveSignalAccount } from "./accounts-hOCHbEhX.js";
|
|
2
|
+
import { d as normalizeSignalMessagingTarget, l as looksLikeUuid } from "./identity-B6O4k8xg.js";
|
|
3
|
+
import { c as getSignalApprovalApprovers, l as signalApprovalAuth, r as markdownToSignalText } from "./message-actions-Bue0g2Kc.js";
|
|
4
|
+
import { a as signalRpcRequest, r as resolveSignalRpcContext } from "./reaction-runtime-api-C_PQ45D9.js";
|
|
5
|
+
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
|
|
6
|
+
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
7
|
+
import { kindFromMime, resolveOutboundAttachmentFromUrl } from "openclaw/plugin-sdk/media-runtime";
|
|
8
|
+
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
|
9
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
10
|
+
import { buildApprovalReactionHint, createApprovalReactionTargetStore, listApprovalReactionBindings, resolveApprovalReactionTarget } from "openclaw/plugin-sdk/approval-reaction-runtime";
|
|
11
|
+
import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
12
|
+
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
13
|
+
import { matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
|
|
14
|
+
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
15
|
+
//#region extensions/signal/src/runtime.ts
|
|
16
|
+
const { setRuntime: setSignalRuntime, getRuntime: getSignalRuntime, tryGetRuntime: getOptionalSignalRuntime, clearRuntime: clearSignalRuntime } = createPluginRuntimeStore({
|
|
17
|
+
pluginId: "signal",
|
|
18
|
+
errorMessage: "Signal runtime not initialized"
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region extensions/signal/src/approval-reactions.ts
|
|
22
|
+
const PERSISTENT_NAMESPACE = "signal.approval-reactions";
|
|
23
|
+
const PERSISTENT_MAX_ENTRIES = 1e3;
|
|
24
|
+
const DEFAULT_REACTION_TARGET_TTL_MS = 1440 * 60 * 1e3;
|
|
25
|
+
let resolverRuntimePromise;
|
|
26
|
+
const signalApprovalReactionTargets = createApprovalReactionTargetStore({
|
|
27
|
+
namespace: PERSISTENT_NAMESPACE,
|
|
28
|
+
maxEntries: PERSISTENT_MAX_ENTRIES,
|
|
29
|
+
defaultTtlMs: DEFAULT_REACTION_TARGET_TTL_MS,
|
|
30
|
+
openStore: (storeParams) => getOptionalSignalRuntime()?.state.openKeyedStore(storeParams),
|
|
31
|
+
logPersistentError: reportPersistentApprovalReactionError,
|
|
32
|
+
readPersistedTarget
|
|
33
|
+
});
|
|
34
|
+
function loadApprovalResolver() {
|
|
35
|
+
resolverRuntimePromise ??= import("./approval-resolver-BR0MioAA.js");
|
|
36
|
+
return resolverRuntimePromise;
|
|
37
|
+
}
|
|
38
|
+
function resolveApprovalKindFromId(approvalId) {
|
|
39
|
+
return approvalId.startsWith("plugin:") ? "plugin" : "exec";
|
|
40
|
+
}
|
|
41
|
+
function resolveApprovalForwardingConfig(params) {
|
|
42
|
+
return params.approvalKind === "plugin" ? params.cfg.approvals?.plugin : params.cfg.approvals?.exec;
|
|
43
|
+
}
|
|
44
|
+
function normalizeApprovalForwardingMode(mode) {
|
|
45
|
+
return mode ?? "session";
|
|
46
|
+
}
|
|
47
|
+
function approvalModeIncludesSession(mode) {
|
|
48
|
+
return mode === "session" || mode === "both";
|
|
49
|
+
}
|
|
50
|
+
function approvalModeIncludesTargets(mode) {
|
|
51
|
+
return mode === "targets" || mode === "both";
|
|
52
|
+
}
|
|
53
|
+
function matchesSignalApprovalReactionFilters(params) {
|
|
54
|
+
return matchesApprovalRequestFilters({
|
|
55
|
+
request: {
|
|
56
|
+
agentId: params.route.agentId,
|
|
57
|
+
sessionKey: params.route.sessionKey
|
|
58
|
+
},
|
|
59
|
+
agentFilter: params.config.agentFilter,
|
|
60
|
+
sessionFilter: params.config.sessionFilter,
|
|
61
|
+
fallbackAgentIdFromSessionKey: true
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function targetAccountMatches(params) {
|
|
65
|
+
const configuredAccountId = normalizeOptionalString(params.configuredAccountId);
|
|
66
|
+
if (!configuredAccountId) return true;
|
|
67
|
+
const routeAccountId = normalizeOptionalString(params.routeAccountId);
|
|
68
|
+
return Boolean(routeAccountId && normalizeAccountId(routeAccountId) === normalizeAccountId(configuredAccountId));
|
|
69
|
+
}
|
|
70
|
+
function hasMatchingSignalApprovalReactionTarget(params) {
|
|
71
|
+
return (params.config.targets ?? []).some((target) => {
|
|
72
|
+
if (normalizeLowercaseStringOrEmpty(target.channel) !== "signal") return false;
|
|
73
|
+
const configuredTo = normalizeSignalMessagingTarget(target.to);
|
|
74
|
+
if (!configuredTo || configuredTo !== params.route.to) return false;
|
|
75
|
+
return targetAccountMatches({
|
|
76
|
+
routeAccountId: params.route.accountId,
|
|
77
|
+
configuredAccountId: target.accountId
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function isSignalApprovalReactionRouteStillEnabled(params) {
|
|
82
|
+
const config = resolveApprovalForwardingConfig({
|
|
83
|
+
cfg: params.cfg,
|
|
84
|
+
approvalKind: params.target.approvalKind
|
|
85
|
+
});
|
|
86
|
+
if (!config?.enabled) return false;
|
|
87
|
+
const mode = normalizeApprovalForwardingMode(config.mode);
|
|
88
|
+
if (params.target.route.deliveryMode === "target") return approvalModeIncludesTargets(mode) && matchesSignalApprovalReactionFilters({
|
|
89
|
+
config,
|
|
90
|
+
route: params.target.route
|
|
91
|
+
}) && hasMatchingSignalApprovalReactionTarget({
|
|
92
|
+
config,
|
|
93
|
+
route: params.target.route
|
|
94
|
+
});
|
|
95
|
+
if (!approvalModeIncludesSession(mode)) return false;
|
|
96
|
+
return matchesSignalApprovalReactionFilters({
|
|
97
|
+
config,
|
|
98
|
+
route: params.target.route
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function resolveSignalApprovalConversationKey(to) {
|
|
102
|
+
return normalizeSignalMessagingTarget(to) ?? null;
|
|
103
|
+
}
|
|
104
|
+
function normalizeSignalApprovalTargetAuthorKey(value) {
|
|
105
|
+
const normalized = normalizeOptionalString(value);
|
|
106
|
+
if (!normalized) return null;
|
|
107
|
+
const withoutSignalPrefix = normalized.replace(/^signal:/i, "").trim();
|
|
108
|
+
if (normalizeLowercaseStringOrEmpty(withoutSignalPrefix).startsWith("uuid:")) {
|
|
109
|
+
const uuid = withoutSignalPrefix.slice(5).trim().toLowerCase();
|
|
110
|
+
return uuid ? `uuid:${uuid}` : null;
|
|
111
|
+
}
|
|
112
|
+
if (looksLikeUuid(withoutSignalPrefix)) return `uuid:${withoutSignalPrefix.toLowerCase()}`;
|
|
113
|
+
return normalizeE164(withoutSignalPrefix);
|
|
114
|
+
}
|
|
115
|
+
function resolveSignalApprovalTargetAuthorKeys(params) {
|
|
116
|
+
const targetAuthorUuid = normalizeOptionalString(params.targetAuthorUuid);
|
|
117
|
+
const keys = [targetAuthorUuid ? `uuid:${targetAuthorUuid.replace(/^uuid:/i, "").trim().toLowerCase()}` : null, params.targetAuthor ? normalizeSignalApprovalTargetAuthorKey(params.targetAuthor) : null].filter((key) => Boolean(key));
|
|
118
|
+
return Array.from(new Set(keys));
|
|
119
|
+
}
|
|
120
|
+
function buildReactionTargetKey(params) {
|
|
121
|
+
const accountId = params.accountId.trim();
|
|
122
|
+
const conversationKey = params.conversationKey.trim();
|
|
123
|
+
const messageId = params.messageId.trim();
|
|
124
|
+
if (!accountId || !conversationKey || !messageId || messageId === "unknown") return null;
|
|
125
|
+
return `${accountId}:${conversationKey}:${messageId}`;
|
|
126
|
+
}
|
|
127
|
+
function reportPersistentApprovalReactionError(error) {
|
|
128
|
+
try {
|
|
129
|
+
getOptionalSignalRuntime()?.logging.getChildLogger({
|
|
130
|
+
plugin: "signal",
|
|
131
|
+
feature: "approval-reaction-state"
|
|
132
|
+
}).warn("Signal persistent approval reaction state failed", { error: String(error) });
|
|
133
|
+
} catch {}
|
|
134
|
+
}
|
|
135
|
+
function readPersistedTarget(target) {
|
|
136
|
+
const value = target;
|
|
137
|
+
if (!value || typeof value.approvalId !== "string" || value.approvalKind !== "exec" && value.approvalKind !== "plugin" || !value.route || value.route.deliveryMode !== "session" && value.route.deliveryMode !== "target" || !Array.isArray(value.targetAuthorKeys) || !Array.isArray(value.allowedDecisions)) return null;
|
|
138
|
+
const targetRouteTo = value.route.deliveryMode === "target" && typeof value.route.to === "string" ? normalizeSignalMessagingTarget(value.route.to) : null;
|
|
139
|
+
if (value.route.deliveryMode === "target" && !targetRouteTo) return null;
|
|
140
|
+
const route = value.route.deliveryMode === "target" ? {
|
|
141
|
+
deliveryMode: "target",
|
|
142
|
+
to: targetRouteTo,
|
|
143
|
+
...typeof value.route.accountId === "string" ? { accountId: value.route.accountId } : {},
|
|
144
|
+
...typeof value.route.agentId === "string" ? { agentId: value.route.agentId } : {},
|
|
145
|
+
...typeof value.route.sessionKey === "string" ? { sessionKey: value.route.sessionKey } : {}
|
|
146
|
+
} : {
|
|
147
|
+
deliveryMode: "session",
|
|
148
|
+
...typeof value.route.agentId === "string" ? { agentId: value.route.agentId } : {},
|
|
149
|
+
...typeof value.route.sessionKey === "string" ? { sessionKey: value.route.sessionKey } : {}
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
approvalId: value.approvalId,
|
|
153
|
+
approvalKind: value.approvalKind,
|
|
154
|
+
allowedDecisions: value.allowedDecisions,
|
|
155
|
+
targetAuthorKeys: value.targetAuthorKeys,
|
|
156
|
+
route
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function listSignalApprovalReactionBindings(allowedDecisions) {
|
|
160
|
+
return listApprovalReactionBindings({ allowedDecisions });
|
|
161
|
+
}
|
|
162
|
+
function buildSignalApprovalReactionHint(allowedDecisions) {
|
|
163
|
+
return buildApprovalReactionHint({ allowedDecisions });
|
|
164
|
+
}
|
|
165
|
+
function insertSignalApprovalReactionHintNearHeader(params) {
|
|
166
|
+
const lines = params.text.split(/\r?\n/);
|
|
167
|
+
const idLineIndex = lines.findIndex((line) => /^ID:\s*\S+/.test(line.trim()));
|
|
168
|
+
if (idLineIndex >= 0) {
|
|
169
|
+
const before = lines.slice(0, idLineIndex + 1).join("\n");
|
|
170
|
+
const after = lines.slice(idLineIndex + 1).join("\n").replace(/^\n+/, "");
|
|
171
|
+
return after ? `${before}\n\n${params.hint}\n\n${after}` : `${before}\n\n${params.hint}`;
|
|
172
|
+
}
|
|
173
|
+
return `${params.hint}\n\n${params.text}`;
|
|
174
|
+
}
|
|
175
|
+
function addSignalApprovalReactionHintToText(params) {
|
|
176
|
+
if (/(^|\n)React with:\s*(\n|$)/i.test(params.text)) return params.text;
|
|
177
|
+
const hint = buildSignalApprovalReactionHint(params.allowedDecisions);
|
|
178
|
+
return hint ? insertSignalApprovalReactionHintNearHeader({
|
|
179
|
+
text: params.text,
|
|
180
|
+
hint
|
|
181
|
+
}) : params.text;
|
|
182
|
+
}
|
|
183
|
+
function normalizeApprovalDecision(value) {
|
|
184
|
+
const normalized = value.trim().toLowerCase();
|
|
185
|
+
if (normalized === "always") return "allow-always";
|
|
186
|
+
if (normalized === "allow-once" || normalized === "allow-always" || normalized === "deny") return normalized;
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
function extractSignalApprovalPromptBinding(text) {
|
|
190
|
+
const allowedDecisions = [];
|
|
191
|
+
let approvalId = "";
|
|
192
|
+
for (const line of text.split(/\r?\n/)) {
|
|
193
|
+
const match = line.match(/\/approve(?:@[^\s]+)?\s+([A-Za-z0-9][A-Za-z0-9._:-]*)\s+(.+)$/i);
|
|
194
|
+
if (!match) continue;
|
|
195
|
+
if (approvalId && match[1] !== approvalId) continue;
|
|
196
|
+
approvalId ||= match[1];
|
|
197
|
+
for (const decisionText of match[2].split(/[\s|,]+/)) {
|
|
198
|
+
const decision = normalizeApprovalDecision(decisionText);
|
|
199
|
+
if (decision && !allowedDecisions.includes(decision)) allowedDecisions.push(decision);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return approvalId && allowedDecisions.length > 0 ? {
|
|
203
|
+
approvalId,
|
|
204
|
+
allowedDecisions
|
|
205
|
+
} : null;
|
|
206
|
+
}
|
|
207
|
+
function buildTargetRoute(params) {
|
|
208
|
+
const to = normalizeSignalMessagingTarget(params.to);
|
|
209
|
+
if (!to) return null;
|
|
210
|
+
const route = {
|
|
211
|
+
deliveryMode: "target",
|
|
212
|
+
to,
|
|
213
|
+
...normalizeOptionalString(params.accountId) ? { accountId: normalizeOptionalString(params.accountId) } : {},
|
|
214
|
+
...normalizeOptionalString(params.agentId) ? { agentId: normalizeOptionalString(params.agentId) } : {},
|
|
215
|
+
...normalizeOptionalString(params.sessionKey) ? { sessionKey: normalizeOptionalString(params.sessionKey) } : {}
|
|
216
|
+
};
|
|
217
|
+
return isSignalApprovalReactionRouteStillEnabled({
|
|
218
|
+
cfg: params.cfg,
|
|
219
|
+
target: {
|
|
220
|
+
approvalKind: resolveApprovalKindFromId(params.approvalId),
|
|
221
|
+
route
|
|
222
|
+
}
|
|
223
|
+
}) ? route : null;
|
|
224
|
+
}
|
|
225
|
+
function shouldAppendSignalApprovalReactionHintForOutboundMessage(params) {
|
|
226
|
+
const binding = extractSignalApprovalPromptBinding(params.text);
|
|
227
|
+
if (!binding) return false;
|
|
228
|
+
if (resolveSignalApprovalTargetAuthorKeys(params).length === 0) return false;
|
|
229
|
+
if (!hasSignalApprovalReactionApprovers({
|
|
230
|
+
cfg: params.cfg,
|
|
231
|
+
accountId: params.accountId
|
|
232
|
+
})) return false;
|
|
233
|
+
return Boolean(buildTargetRoute({
|
|
234
|
+
cfg: params.cfg,
|
|
235
|
+
accountId: params.accountId,
|
|
236
|
+
to: params.to,
|
|
237
|
+
approvalId: binding.approvalId,
|
|
238
|
+
agentId: params.agentId,
|
|
239
|
+
sessionKey: params.sessionKey
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
function appendSignalApprovalReactionHintForOutboundMessage(params) {
|
|
243
|
+
const binding = extractSignalApprovalPromptBinding(params.text);
|
|
244
|
+
if (!binding || !shouldAppendSignalApprovalReactionHintForOutboundMessage({
|
|
245
|
+
...params,
|
|
246
|
+
text: params.text
|
|
247
|
+
})) return params.text;
|
|
248
|
+
return addSignalApprovalReactionHintToText({
|
|
249
|
+
text: params.text,
|
|
250
|
+
allowedDecisions: binding.allowedDecisions
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function hasSignalApprovalReactionApprovers(params) {
|
|
254
|
+
return getSignalApprovalApprovers(params).length > 0;
|
|
255
|
+
}
|
|
256
|
+
function registerSignalApprovalReactionTarget(params) {
|
|
257
|
+
const key = buildReactionTargetKey(params);
|
|
258
|
+
const approvalId = params.approvalId.trim();
|
|
259
|
+
const targetAuthorKeys = Array.from(new Set(params.targetAuthorKeys.map((entry) => normalizeSignalApprovalTargetAuthorKey(entry)).filter((entry) => Boolean(entry))));
|
|
260
|
+
const allowedDecisions = listSignalApprovalReactionBindings(params.allowedDecisions).map((binding) => binding.decision);
|
|
261
|
+
if (!params.routeAllowed || !key || !approvalId || allowedDecisions.length === 0) return null;
|
|
262
|
+
if (targetAuthorKeys.length === 0) return null;
|
|
263
|
+
const route = params.route.deliveryMode === "target" ? {
|
|
264
|
+
deliveryMode: "target",
|
|
265
|
+
to: params.route.to,
|
|
266
|
+
...normalizeOptionalString(params.route.accountId) ? { accountId: normalizeOptionalString(params.route.accountId) } : {},
|
|
267
|
+
...normalizeOptionalString(params.route.agentId) ? { agentId: normalizeOptionalString(params.route.agentId) } : {},
|
|
268
|
+
...normalizeOptionalString(params.route.sessionKey) ? { sessionKey: normalizeOptionalString(params.route.sessionKey) } : {}
|
|
269
|
+
} : {
|
|
270
|
+
deliveryMode: "session",
|
|
271
|
+
...normalizeOptionalString(params.route.agentId) ? { agentId: normalizeOptionalString(params.route.agentId) } : {},
|
|
272
|
+
...normalizeOptionalString(params.route.sessionKey) ? { sessionKey: normalizeOptionalString(params.route.sessionKey) } : {}
|
|
273
|
+
};
|
|
274
|
+
const target = {
|
|
275
|
+
approvalId,
|
|
276
|
+
approvalKind: resolveApprovalKindFromId(approvalId),
|
|
277
|
+
allowedDecisions,
|
|
278
|
+
targetAuthorKeys,
|
|
279
|
+
route
|
|
280
|
+
};
|
|
281
|
+
signalApprovalReactionTargets.register(key, target, { ttlMs: params.ttlMs });
|
|
282
|
+
return target;
|
|
283
|
+
}
|
|
284
|
+
function registerSignalApprovalReactionTargetForOutboundMessage(params) {
|
|
285
|
+
const binding = extractSignalApprovalPromptBinding(params.text);
|
|
286
|
+
if (!binding) return false;
|
|
287
|
+
const conversationKey = resolveSignalApprovalConversationKey(params.to);
|
|
288
|
+
if (!conversationKey) return false;
|
|
289
|
+
const route = buildTargetRoute({
|
|
290
|
+
cfg: params.cfg,
|
|
291
|
+
accountId: params.accountId,
|
|
292
|
+
to: params.to,
|
|
293
|
+
approvalId: binding.approvalId,
|
|
294
|
+
agentId: params.agentId,
|
|
295
|
+
sessionKey: params.sessionKey
|
|
296
|
+
});
|
|
297
|
+
if (!route) return false;
|
|
298
|
+
return Boolean(registerSignalApprovalReactionTarget({
|
|
299
|
+
accountId: params.accountId,
|
|
300
|
+
conversationKey,
|
|
301
|
+
messageId: params.messageId,
|
|
302
|
+
approvalId: binding.approvalId,
|
|
303
|
+
allowedDecisions: binding.allowedDecisions,
|
|
304
|
+
targetAuthorKeys: resolveSignalApprovalTargetAuthorKeys(params),
|
|
305
|
+
route,
|
|
306
|
+
routeAllowed: true,
|
|
307
|
+
ttlMs: params.ttlMs
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
function unregisterSignalApprovalReactionTarget(params) {
|
|
311
|
+
const key = buildReactionTargetKey(params);
|
|
312
|
+
if (!key) return;
|
|
313
|
+
signalApprovalReactionTargets.delete(key);
|
|
314
|
+
}
|
|
315
|
+
function resolveTarget(params) {
|
|
316
|
+
const target = params.target;
|
|
317
|
+
if (!target) return null;
|
|
318
|
+
if (params.targetAuthorKeys.length === 0 || !params.targetAuthorKeys.some((key) => target.targetAuthorKeys.includes(key))) return null;
|
|
319
|
+
const resolved = resolveApprovalReactionTarget({
|
|
320
|
+
target,
|
|
321
|
+
reactionKey: params.reactionKey
|
|
322
|
+
});
|
|
323
|
+
if (!resolved?.route) return null;
|
|
324
|
+
return {
|
|
325
|
+
approvalId: resolved.approvalId,
|
|
326
|
+
approvalKind: resolved.approvalKind,
|
|
327
|
+
decision: resolved.decision,
|
|
328
|
+
route: resolved.route
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
async function resolveSignalApprovalReactionTargetWithPersistence(params) {
|
|
332
|
+
const key = buildReactionTargetKey(params);
|
|
333
|
+
if (!key) return null;
|
|
334
|
+
const targetAuthorKeys = resolveSignalApprovalTargetAuthorKeys(params);
|
|
335
|
+
if (targetAuthorKeys.length === 0) return null;
|
|
336
|
+
return resolveTarget({
|
|
337
|
+
target: await signalApprovalReactionTargets.lookup(key),
|
|
338
|
+
reactionKey: params.reactionKey,
|
|
339
|
+
targetAuthorKeys
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
async function maybeResolveSignalApprovalReaction(params) {
|
|
343
|
+
const target = await resolveSignalApprovalReactionTargetWithPersistence({
|
|
344
|
+
accountId: params.accountId,
|
|
345
|
+
conversationKey: params.conversationKey,
|
|
346
|
+
messageId: params.messageId,
|
|
347
|
+
reactionKey: params.reactionKey,
|
|
348
|
+
targetAuthor: params.targetAuthor,
|
|
349
|
+
targetAuthorUuid: params.targetAuthorUuid
|
|
350
|
+
});
|
|
351
|
+
if (!target) return false;
|
|
352
|
+
if (!isSignalApprovalReactionRouteStillEnabled({
|
|
353
|
+
cfg: params.cfg,
|
|
354
|
+
target
|
|
355
|
+
})) {
|
|
356
|
+
params.logVerboseMessage?.(`signal: approval reaction denied id=${target.approvalId}; approval route is no longer enabled`);
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
const actorId = params.actorId?.trim();
|
|
360
|
+
if (!actorId) {
|
|
361
|
+
params.logVerboseMessage?.(`signal: approval reaction ignored for ${target.approvalId}; missing actor identity`);
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
if (getSignalApprovalApprovers({
|
|
365
|
+
cfg: params.cfg,
|
|
366
|
+
accountId: params.accountId
|
|
367
|
+
}).length === 0) {
|
|
368
|
+
params.logVerboseMessage?.(`signal: approval reaction denied id=${target.approvalId}; reactions require explicit approvers`);
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
if (!signalApprovalAuth.authorizeActorAction({
|
|
372
|
+
cfg: params.cfg,
|
|
373
|
+
accountId: params.accountId,
|
|
374
|
+
senderId: actorId,
|
|
375
|
+
action: "approve",
|
|
376
|
+
approvalKind: target.approvalKind
|
|
377
|
+
}).authorized) {
|
|
378
|
+
params.logVerboseMessage?.(`signal: approval reaction denied id=${target.approvalId} sender=${actorId}`);
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
const { isApprovalNotFoundError, resolveSignalApproval } = await loadApprovalResolver();
|
|
382
|
+
try {
|
|
383
|
+
await resolveSignalApproval({
|
|
384
|
+
cfg: params.cfg,
|
|
385
|
+
approvalId: target.approvalId,
|
|
386
|
+
decision: target.decision,
|
|
387
|
+
senderId: actorId,
|
|
388
|
+
gatewayUrl: params.gatewayUrl
|
|
389
|
+
});
|
|
390
|
+
params.logVerboseMessage?.(`signal: approval reaction resolved id=${target.approvalId} sender=${actorId} decision=${target.decision}`);
|
|
391
|
+
return true;
|
|
392
|
+
} catch (error) {
|
|
393
|
+
if (isApprovalNotFoundError(error)) {
|
|
394
|
+
unregisterSignalApprovalReactionTarget({
|
|
395
|
+
accountId: params.accountId,
|
|
396
|
+
conversationKey: params.conversationKey,
|
|
397
|
+
messageId: params.messageId
|
|
398
|
+
});
|
|
399
|
+
params.logVerboseMessage?.(`signal: approval reaction ignored for expired approval id=${target.approvalId} sender=${actorId}`);
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
params.logVerboseMessage?.(`signal: approval reaction failed id=${target.approvalId} sender=${actorId}: ${String(error)}`);
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region extensions/signal/src/send.ts
|
|
408
|
+
async function resolveSignalRpcAccountInfo(opts) {
|
|
409
|
+
if (opts.baseUrl?.trim() && opts.account?.trim()) return;
|
|
410
|
+
if (!opts.cfg) throw new Error("Signal RPC account resolution requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.");
|
|
411
|
+
return resolveSignalAccount({
|
|
412
|
+
cfg: requireRuntimeConfig(opts.cfg, "Signal RPC account resolution"),
|
|
413
|
+
accountId: opts.accountId
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
function parseTarget(raw) {
|
|
417
|
+
let value = raw.trim();
|
|
418
|
+
if (!value) throw new Error("Signal recipient is required");
|
|
419
|
+
if (normalizeLowercaseStringOrEmpty(value).startsWith("signal:")) value = value.slice(7).trim();
|
|
420
|
+
const normalized = normalizeLowercaseStringOrEmpty(value);
|
|
421
|
+
if (normalized.startsWith("group:")) return {
|
|
422
|
+
type: "group",
|
|
423
|
+
groupId: value.slice(6).trim()
|
|
424
|
+
};
|
|
425
|
+
if (normalized.startsWith("username:")) return {
|
|
426
|
+
type: "username",
|
|
427
|
+
username: value.slice(9).trim()
|
|
428
|
+
};
|
|
429
|
+
if (normalized.startsWith("u:")) return {
|
|
430
|
+
type: "username",
|
|
431
|
+
username: value.trim()
|
|
432
|
+
};
|
|
433
|
+
return {
|
|
434
|
+
type: "recipient",
|
|
435
|
+
recipient: value
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function buildTargetParams(target, allow) {
|
|
439
|
+
if (target.type === "recipient") {
|
|
440
|
+
if (!allow.recipient) return null;
|
|
441
|
+
return { recipient: [target.recipient] };
|
|
442
|
+
}
|
|
443
|
+
if (target.type === "group") {
|
|
444
|
+
if (!allow.group) return null;
|
|
445
|
+
return { groupId: target.groupId };
|
|
446
|
+
}
|
|
447
|
+
if (target.type === "username") {
|
|
448
|
+
if (!allow.username) return null;
|
|
449
|
+
return { username: [target.username] };
|
|
450
|
+
}
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
function createSignalSendReceipt(params) {
|
|
454
|
+
const messageId = params.messageId.trim();
|
|
455
|
+
const results = messageId && messageId !== "unknown" ? [{
|
|
456
|
+
channel: "signal",
|
|
457
|
+
messageId,
|
|
458
|
+
meta: { targetType: params.target.type }
|
|
459
|
+
}] : [];
|
|
460
|
+
if (results[0]) {
|
|
461
|
+
if (params.timestamp != null) results[0].timestamp = params.timestamp;
|
|
462
|
+
if (params.target.type === "group") results[0].chatId = params.target.groupId;
|
|
463
|
+
else if (params.target.type === "recipient") results[0].toJid = params.target.recipient;
|
|
464
|
+
else results[0].toJid = params.target.username;
|
|
465
|
+
}
|
|
466
|
+
return createMessageReceiptFromOutboundResults({
|
|
467
|
+
results,
|
|
468
|
+
kind: params.kind
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
async function sendMessageSignal(to, text, opts) {
|
|
472
|
+
const cfg = requireRuntimeConfig(opts.cfg, "Signal send");
|
|
473
|
+
const apiMode = cfg.channels?.signal?.apiMode;
|
|
474
|
+
const accountInfo = resolveSignalAccount({
|
|
475
|
+
cfg,
|
|
476
|
+
accountId: opts.accountId
|
|
477
|
+
});
|
|
478
|
+
const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
|
|
479
|
+
const target = parseTarget(to);
|
|
480
|
+
const outboundText = appendSignalApprovalReactionHintForOutboundMessage({
|
|
481
|
+
cfg,
|
|
482
|
+
accountId: accountInfo.accountId,
|
|
483
|
+
to,
|
|
484
|
+
text: text ?? "",
|
|
485
|
+
targetAuthor: account
|
|
486
|
+
});
|
|
487
|
+
let message = outboundText;
|
|
488
|
+
let messageFromPlaceholder = false;
|
|
489
|
+
let textStyles = [];
|
|
490
|
+
const textMode = opts.textMode ?? "markdown";
|
|
491
|
+
const maxBytes = (() => {
|
|
492
|
+
if (typeof opts.maxBytes === "number") return opts.maxBytes;
|
|
493
|
+
if (typeof accountInfo.config.mediaMaxMb === "number") return accountInfo.config.mediaMaxMb * 1024 * 1024;
|
|
494
|
+
if (typeof cfg.agents?.defaults?.mediaMaxMb === "number") return cfg.agents.defaults.mediaMaxMb * 1024 * 1024;
|
|
495
|
+
return 8 * 1024 * 1024;
|
|
496
|
+
})();
|
|
497
|
+
let attachments;
|
|
498
|
+
if (opts.mediaUrl?.trim()) {
|
|
499
|
+
const resolved = await resolveOutboundAttachmentFromUrl(opts.mediaUrl.trim(), maxBytes, {
|
|
500
|
+
mediaAccess: opts.mediaAccess,
|
|
501
|
+
localRoots: opts.mediaLocalRoots,
|
|
502
|
+
readFile: opts.mediaReadFile
|
|
503
|
+
});
|
|
504
|
+
attachments = [resolved.path];
|
|
505
|
+
const kind = kindFromMime(resolved.contentType ?? void 0);
|
|
506
|
+
if (!message && kind) {
|
|
507
|
+
message = kind === "image" ? "<media:image>" : `<media:${kind}>`;
|
|
508
|
+
messageFromPlaceholder = true;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (message.trim() && !messageFromPlaceholder) if (textMode === "plain") textStyles = opts.textStyles ?? [];
|
|
512
|
+
else {
|
|
513
|
+
const tableMode = resolveMarkdownTableMode({
|
|
514
|
+
cfg,
|
|
515
|
+
channel: "signal",
|
|
516
|
+
accountId: accountInfo.accountId
|
|
517
|
+
});
|
|
518
|
+
const formatted = markdownToSignalText(message, { tableMode });
|
|
519
|
+
message = formatted.text;
|
|
520
|
+
textStyles = formatted.styles;
|
|
521
|
+
}
|
|
522
|
+
if (!message.trim() && (!attachments || attachments.length === 0)) throw new Error("Signal send requires text or media");
|
|
523
|
+
const params = { message };
|
|
524
|
+
if (textStyles.length > 0) params["text-style"] = textStyles.map((style) => `${style.start}:${style.length}:${style.style}`);
|
|
525
|
+
if (account) params.account = account;
|
|
526
|
+
if (attachments && attachments.length > 0) params.attachments = attachments;
|
|
527
|
+
const targetParams = buildTargetParams(target, {
|
|
528
|
+
recipient: true,
|
|
529
|
+
group: true,
|
|
530
|
+
username: true
|
|
531
|
+
});
|
|
532
|
+
if (!targetParams) throw new Error("Signal recipient is required");
|
|
533
|
+
Object.assign(params, targetParams);
|
|
534
|
+
const timestamp = (await signalRpcRequest("send", params, {
|
|
535
|
+
baseUrl,
|
|
536
|
+
timeoutMs: opts.timeoutMs,
|
|
537
|
+
apiMode
|
|
538
|
+
}))?.timestamp;
|
|
539
|
+
const messageId = timestamp ? String(timestamp) : "unknown";
|
|
540
|
+
registerSignalApprovalReactionTargetForOutboundMessage({
|
|
541
|
+
cfg,
|
|
542
|
+
accountId: accountInfo.accountId,
|
|
543
|
+
to,
|
|
544
|
+
messageId,
|
|
545
|
+
text: outboundText,
|
|
546
|
+
targetAuthor: account
|
|
547
|
+
});
|
|
548
|
+
return {
|
|
549
|
+
messageId,
|
|
550
|
+
timestamp,
|
|
551
|
+
receipt: createSignalSendReceipt({
|
|
552
|
+
messageId,
|
|
553
|
+
target,
|
|
554
|
+
kind: attachments && attachments.length > 0 ? "media" : "text",
|
|
555
|
+
...timestamp != null ? { timestamp } : {}
|
|
556
|
+
})
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
async function sendTypingSignal(to, opts) {
|
|
560
|
+
const accountInfo = await resolveSignalRpcAccountInfo(opts);
|
|
561
|
+
const cfg = requireRuntimeConfig(opts.cfg, "Signal typing");
|
|
562
|
+
const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
|
|
563
|
+
const targetParams = buildTargetParams(parseTarget(to), {
|
|
564
|
+
recipient: true,
|
|
565
|
+
group: true
|
|
566
|
+
});
|
|
567
|
+
if (!targetParams) return false;
|
|
568
|
+
const params = { ...targetParams };
|
|
569
|
+
if (account) params.account = account;
|
|
570
|
+
if (opts.stop) params.stop = true;
|
|
571
|
+
await signalRpcRequest("sendTyping", params, {
|
|
572
|
+
baseUrl,
|
|
573
|
+
timeoutMs: opts.timeoutMs,
|
|
574
|
+
apiMode: cfg.channels?.signal?.apiMode
|
|
575
|
+
});
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
async function sendReadReceiptSignal(to, targetTimestamp, opts) {
|
|
579
|
+
if (!Number.isFinite(targetTimestamp) || targetTimestamp <= 0) return false;
|
|
580
|
+
const accountInfo = await resolveSignalRpcAccountInfo(opts);
|
|
581
|
+
const cfg = requireRuntimeConfig(opts.cfg, "Signal read receipt");
|
|
582
|
+
const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
|
|
583
|
+
const targetParams = buildTargetParams(parseTarget(to), { recipient: true });
|
|
584
|
+
if (!targetParams) return false;
|
|
585
|
+
const params = {
|
|
586
|
+
...targetParams,
|
|
587
|
+
targetTimestamp,
|
|
588
|
+
type: opts.type ?? "read"
|
|
589
|
+
};
|
|
590
|
+
if (account) params.account = account;
|
|
591
|
+
await signalRpcRequest("sendReceipt", params, {
|
|
592
|
+
baseUrl,
|
|
593
|
+
timeoutMs: opts.timeoutMs,
|
|
594
|
+
apiMode: cfg.channels?.signal?.apiMode
|
|
595
|
+
});
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
//#endregion
|
|
599
|
+
export { maybeResolveSignalApprovalReaction as a, resolveSignalApprovalTargetAuthorKeys as c, hasSignalApprovalReactionApprovers as i, unregisterSignalApprovalReactionTarget as l, sendReadReceiptSignal as n, registerSignalApprovalReactionTarget as o, sendTypingSignal as r, resolveSignalApprovalConversationKey as s, sendMessageSignal as t, setSignalRuntime as u };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
//#region extensions/signal/setup-entry.ts
|
|
3
|
+
var setup_entry_default = defineBundledChannelSetupEntry({
|
|
4
|
+
importMetaUrl: import.meta.url,
|
|
5
|
+
plugin: {
|
|
6
|
+
specifier: "./api.js",
|
|
7
|
+
exportName: "signalSetupPlugin"
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { setup_entry_default as default };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
|
2
|
+
Copyright (c) 2013 Arnout Kazemier and contributors
|
|
3
|
+
Copyright (c) 2016 Luigi Pinca and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|