@openclaw/signal 0.0.0 → 2026.6.11-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/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,619 @@
|
|
|
1
|
+
import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId } from "./accounts-hOCHbEhX.js";
|
|
2
|
+
import { c as resolveSignalSender, d as normalizeSignalMessagingTarget, l as looksLikeUuid, o as resolveSignalPeerId, s as resolveSignalRecipient, u as looksLikeSignalTargetId } from "./identity-B6O4k8xg.js";
|
|
3
|
+
import { i as markdownToSignalTextChunks, n as resolveSignalReactionLevel, o as shouldSuppressLocalSignalExecApprovalPrompt, s as signalApprovalCapability, t as signalMessageActions } from "./message-actions-Bue0g2Kc.js";
|
|
4
|
+
import { t as SignalChannelConfigSchema } from "./config-schema-BiojLEsX.js";
|
|
5
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
|
6
|
+
import { buildDmGroupAccountAllowlistAdapter } from "openclaw/plugin-sdk/allowlist-config-edit";
|
|
7
|
+
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
8
|
+
import { defineChannelMessageAdapter, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
|
|
9
|
+
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
10
|
+
import { attachChannelToResult, attachChannelToResults } from "openclaw/plugin-sdk/channel-send-result";
|
|
11
|
+
import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
|
|
12
|
+
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
13
|
+
import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
|
|
14
|
+
import { chunkText, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
|
|
15
|
+
import { buildOutboundBaseSessionKey } from "openclaw/plugin-sdk/routing";
|
|
16
|
+
import { buildBaseChannelStatusSummary, collectStatusIssuesFromLastError, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
|
|
17
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringifiedEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
18
|
+
import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
19
|
+
import { createChannelPluginBase, getChatChannelMeta } from "openclaw/plugin-sdk/core";
|
|
20
|
+
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createCliPathTextInput, createDelegatedSetupWizardProxy, createDelegatedTextInputShouldPrompt, createPatchedAccountSetupAdapter, createSetupInputPresenceValidator, createSetupTranslator, mergeAllowFromEntries, parseSetupEntriesAllowingWildcard, patchChannelConfigForAccount, promptParsedAllowFromForAccount, setAccountAllowFromForChannel, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup-runtime";
|
|
21
|
+
import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
|
22
|
+
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
23
|
+
import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
24
|
+
import { createRestrictSendersChannelSecurity } from "openclaw/plugin-sdk/channel-policy";
|
|
25
|
+
//#region extensions/signal/src/outbound-session.ts
|
|
26
|
+
function resolveSignalOutboundTarget(target) {
|
|
27
|
+
const stripped = target.replace(/^signal:/i, "").trim();
|
|
28
|
+
const lowered = normalizeLowercaseStringOrEmpty(stripped);
|
|
29
|
+
if (lowered.startsWith("group:")) {
|
|
30
|
+
const groupId = stripped.slice(6).trim();
|
|
31
|
+
if (!groupId) return null;
|
|
32
|
+
return {
|
|
33
|
+
peer: {
|
|
34
|
+
kind: "group",
|
|
35
|
+
id: groupId
|
|
36
|
+
},
|
|
37
|
+
chatType: "group",
|
|
38
|
+
from: `group:${groupId}`,
|
|
39
|
+
to: `group:${groupId}`
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
let recipient = stripped.trim();
|
|
43
|
+
if (lowered.startsWith("username:")) recipient = stripped.slice(9).trim();
|
|
44
|
+
else if (lowered.startsWith("u:")) recipient = stripped.slice(2).trim();
|
|
45
|
+
if (!recipient) return null;
|
|
46
|
+
const uuidCandidate = normalizeLowercaseStringOrEmpty(recipient).startsWith("uuid:") ? recipient.slice(5) : recipient;
|
|
47
|
+
const sender = resolveSignalSender({
|
|
48
|
+
sourceUuid: looksLikeUuid(uuidCandidate) ? uuidCandidate : null,
|
|
49
|
+
sourceNumber: looksLikeUuid(uuidCandidate) ? null : recipient
|
|
50
|
+
});
|
|
51
|
+
const peerId = sender ? resolveSignalPeerId(sender) : recipient;
|
|
52
|
+
const displayRecipient = sender ? resolveSignalRecipient(sender) : recipient;
|
|
53
|
+
return {
|
|
54
|
+
peer: {
|
|
55
|
+
kind: "direct",
|
|
56
|
+
id: peerId
|
|
57
|
+
},
|
|
58
|
+
chatType: "direct",
|
|
59
|
+
from: `signal:${displayRecipient}`,
|
|
60
|
+
to: `signal:${displayRecipient}`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region extensions/signal/src/setup-core.ts
|
|
65
|
+
const t = createSetupTranslator();
|
|
66
|
+
const channel = "signal";
|
|
67
|
+
const MIN_E164_DIGITS = 5;
|
|
68
|
+
const MAX_E164_DIGITS = 15;
|
|
69
|
+
const DIGITS_ONLY = /^\d+$/;
|
|
70
|
+
const INVALID_SIGNAL_ACCOUNT_ERROR = "Invalid E.164 phone number (must start with + and country code, e.g. +15555550123)";
|
|
71
|
+
function normalizeSignalAccountInput(value) {
|
|
72
|
+
const trimmed = normalizeOptionalString(value);
|
|
73
|
+
if (!trimmed) return null;
|
|
74
|
+
const digits = normalizeE164(trimmed).slice(1);
|
|
75
|
+
if (!DIGITS_ONLY.test(digits)) return null;
|
|
76
|
+
if (digits.length < MIN_E164_DIGITS || digits.length > MAX_E164_DIGITS) return null;
|
|
77
|
+
return `+${digits}`;
|
|
78
|
+
}
|
|
79
|
+
function isUuidLike(value) {
|
|
80
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
81
|
+
}
|
|
82
|
+
function parseSignalAllowFromEntries(raw) {
|
|
83
|
+
return parseSetupEntriesAllowingWildcard(raw, (entry) => {
|
|
84
|
+
if (normalizeLowercaseStringOrEmpty(entry).startsWith("uuid:")) {
|
|
85
|
+
const id = entry.slice(5).trim();
|
|
86
|
+
if (!id) return { error: "Invalid uuid entry" };
|
|
87
|
+
return { value: `uuid:${id}` };
|
|
88
|
+
}
|
|
89
|
+
if (isUuidLike(entry)) return { value: `uuid:${entry}` };
|
|
90
|
+
const normalized = normalizeSignalAccountInput(entry);
|
|
91
|
+
if (!normalized) return { error: `Invalid entry: ${entry}` };
|
|
92
|
+
return { value: normalized };
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function buildSignalSetupPatch(input) {
|
|
96
|
+
return {
|
|
97
|
+
...input.signalNumber ? { account: input.signalNumber } : {},
|
|
98
|
+
...input.cliPath ? { cliPath: input.cliPath } : {},
|
|
99
|
+
...input.httpUrl ? { httpUrl: input.httpUrl } : {},
|
|
100
|
+
...input.httpHost ? { httpHost: input.httpHost } : {},
|
|
101
|
+
...input.httpPort ? { httpPort: Number(input.httpPort) } : {}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async function promptSignalAllowFrom(params) {
|
|
105
|
+
return promptParsedAllowFromForAccount({
|
|
106
|
+
cfg: params.cfg,
|
|
107
|
+
accountId: params.accountId,
|
|
108
|
+
defaultAccountId: resolveDefaultSignalAccountId(params.cfg),
|
|
109
|
+
prompter: params.prompter,
|
|
110
|
+
noteTitle: t("wizard.signal.allowlistTitle"),
|
|
111
|
+
noteLines: [
|
|
112
|
+
t("wizard.signal.allowlistIntro"),
|
|
113
|
+
t("wizard.signal.examples"),
|
|
114
|
+
"- +15555550123",
|
|
115
|
+
"- uuid:123e4567-e89b-12d3-a456-426614174000",
|
|
116
|
+
t("wizard.signal.multipleEntries"),
|
|
117
|
+
`Docs: ${formatDocsLink("/signal", "signal")}`
|
|
118
|
+
],
|
|
119
|
+
message: t("wizard.signal.allowFromPrompt"),
|
|
120
|
+
placeholder: "+15555550123, uuid:123e4567-e89b-12d3-a456-426614174000",
|
|
121
|
+
parseEntries: parseSignalAllowFromEntries,
|
|
122
|
+
getExistingAllowFrom: ({ cfg, accountId }) => resolveSignalAccount({
|
|
123
|
+
cfg,
|
|
124
|
+
accountId
|
|
125
|
+
}).config.allowFrom ?? [],
|
|
126
|
+
applyAllowFrom: ({ cfg, accountId, allowFrom }) => setAccountAllowFromForChannel({
|
|
127
|
+
cfg,
|
|
128
|
+
channel,
|
|
129
|
+
accountId,
|
|
130
|
+
allowFrom
|
|
131
|
+
})
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const signalDmPolicy = {
|
|
135
|
+
label: "Signal",
|
|
136
|
+
channel,
|
|
137
|
+
policyKey: "channels.signal.dmPolicy",
|
|
138
|
+
allowFromKey: "channels.signal.allowFrom",
|
|
139
|
+
resolveConfigKeys: (cfg, accountId) => (accountId ?? resolveDefaultSignalAccountId(cfg)) !== DEFAULT_ACCOUNT_ID$1 ? {
|
|
140
|
+
policyKey: `channels.signal.accounts.${accountId ?? resolveDefaultSignalAccountId(cfg)}.dmPolicy`,
|
|
141
|
+
allowFromKey: `channels.signal.accounts.${accountId ?? resolveDefaultSignalAccountId(cfg)}.allowFrom`
|
|
142
|
+
} : {
|
|
143
|
+
policyKey: "channels.signal.dmPolicy",
|
|
144
|
+
allowFromKey: "channels.signal.allowFrom"
|
|
145
|
+
},
|
|
146
|
+
getCurrent: (cfg, accountId) => resolveSignalAccount({
|
|
147
|
+
cfg,
|
|
148
|
+
accountId: accountId ?? resolveDefaultSignalAccountId(cfg)
|
|
149
|
+
}).config.dmPolicy ?? "pairing",
|
|
150
|
+
setPolicy: (cfg, policy, accountId) => patchChannelConfigForAccount({
|
|
151
|
+
cfg,
|
|
152
|
+
channel,
|
|
153
|
+
accountId: accountId ?? resolveDefaultSignalAccountId(cfg),
|
|
154
|
+
patch: policy === "open" ? {
|
|
155
|
+
dmPolicy: "open",
|
|
156
|
+
allowFrom: mergeAllowFromEntries(resolveSignalAccount({
|
|
157
|
+
cfg,
|
|
158
|
+
accountId: accountId ?? resolveDefaultSignalAccountId(cfg)
|
|
159
|
+
}).config.allowFrom, ["*"])
|
|
160
|
+
} : { dmPolicy: policy }
|
|
161
|
+
}),
|
|
162
|
+
promptAllowFrom: promptSignalAllowFrom
|
|
163
|
+
};
|
|
164
|
+
function resolveSignalCliPath(params) {
|
|
165
|
+
return (typeof params.credentialValues.cliPath === "string" ? params.credentialValues.cliPath : void 0) ?? resolveSignalAccount({
|
|
166
|
+
cfg: params.cfg,
|
|
167
|
+
accountId: params.accountId
|
|
168
|
+
}).config.cliPath ?? "signal-cli";
|
|
169
|
+
}
|
|
170
|
+
function createSignalCliPathTextInput(shouldPrompt) {
|
|
171
|
+
return createCliPathTextInput({
|
|
172
|
+
inputKey: "cliPath",
|
|
173
|
+
message: "signal-cli path",
|
|
174
|
+
resolvePath: ({ cfg, accountId, credentialValues }) => resolveSignalCliPath({
|
|
175
|
+
cfg,
|
|
176
|
+
accountId,
|
|
177
|
+
credentialValues
|
|
178
|
+
}),
|
|
179
|
+
shouldPrompt,
|
|
180
|
+
helpTitle: "Signal",
|
|
181
|
+
helpLines: ["signal-cli not found. Install it, then rerun this step or set channels.signal.cliPath."]
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const signalNumberTextInput = {
|
|
185
|
+
inputKey: "signalNumber",
|
|
186
|
+
message: t("wizard.signal.botNumberPrompt"),
|
|
187
|
+
currentValue: ({ cfg, accountId }) => normalizeSignalAccountInput(resolveSignalAccount({
|
|
188
|
+
cfg,
|
|
189
|
+
accountId
|
|
190
|
+
}).config.account) ?? void 0,
|
|
191
|
+
keepPrompt: (value) => t("wizard.signal.accountKeep", { value }),
|
|
192
|
+
validate: ({ value }) => normalizeSignalAccountInput(value) ? void 0 : INVALID_SIGNAL_ACCOUNT_ERROR,
|
|
193
|
+
normalizeValue: ({ value }) => normalizeSignalAccountInput(value) ?? value
|
|
194
|
+
};
|
|
195
|
+
const signalCompletionNote = {
|
|
196
|
+
title: t("wizard.signal.nextStepsTitle"),
|
|
197
|
+
lines: [
|
|
198
|
+
t("wizard.signal.nextLinkDevice"),
|
|
199
|
+
t("wizard.signal.nextScanQr"),
|
|
200
|
+
`Then run: ${formatCliCommand("openclaw gateway call channels.status --params '{\"probe\":true}'")}`,
|
|
201
|
+
`Docs: ${formatDocsLink("/signal", "signal")}`
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
const signalSetupAdapter = createPatchedAccountSetupAdapter({
|
|
205
|
+
channelKey: channel,
|
|
206
|
+
validateInput: createSetupInputPresenceValidator({ validate: ({ input }) => {
|
|
207
|
+
if (!input.signalNumber && !input.httpUrl && !input.httpHost && !input.httpPort && !input.cliPath) return "Signal requires --signal-number or --http-url/--http-host/--http-port/--cli-path.";
|
|
208
|
+
return null;
|
|
209
|
+
} }),
|
|
210
|
+
buildPatch: (input) => buildSignalSetupPatch(input)
|
|
211
|
+
});
|
|
212
|
+
function createSignalSetupWizardProxy(loadWizard) {
|
|
213
|
+
return createDelegatedSetupWizardProxy({
|
|
214
|
+
channel,
|
|
215
|
+
loadWizard,
|
|
216
|
+
status: {
|
|
217
|
+
configuredLabel: t("wizard.channels.statusConfigured"),
|
|
218
|
+
unconfiguredLabel: t("wizard.channels.statusNeedsSetup"),
|
|
219
|
+
configuredHint: t("wizard.channels.statusSignalCliFound"),
|
|
220
|
+
unconfiguredHint: t("wizard.channels.statusSignalCliMissing"),
|
|
221
|
+
configuredScore: 1,
|
|
222
|
+
unconfiguredScore: 0
|
|
223
|
+
},
|
|
224
|
+
delegatePrepare: true,
|
|
225
|
+
credentials: [],
|
|
226
|
+
textInputs: [createSignalCliPathTextInput(createDelegatedTextInputShouldPrompt({
|
|
227
|
+
loadWizard,
|
|
228
|
+
inputKey: "cliPath"
|
|
229
|
+
})), signalNumberTextInput],
|
|
230
|
+
completionNote: signalCompletionNote,
|
|
231
|
+
dmPolicy: signalDmPolicy,
|
|
232
|
+
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region extensions/signal/src/shared.ts
|
|
237
|
+
const SIGNAL_CHANNEL = "signal";
|
|
238
|
+
async function loadSignalChannelRuntime() {
|
|
239
|
+
return await import("./channel.runtime-B0-YpkC4.js");
|
|
240
|
+
}
|
|
241
|
+
const signalSetupWizard = createSignalSetupWizardProxy(async () => (await loadSignalChannelRuntime()).signalSetupWizard);
|
|
242
|
+
const signalConfigAdapter = createScopedChannelConfigAdapter({
|
|
243
|
+
sectionKey: SIGNAL_CHANNEL,
|
|
244
|
+
listAccountIds: (cfg) => listSignalAccountIds(cfg),
|
|
245
|
+
resolveAccount: adaptScopedAccountAccessor((params) => resolveSignalAccount(params)),
|
|
246
|
+
defaultAccountId: (cfg) => resolveDefaultSignalAccountId(cfg),
|
|
247
|
+
clearBaseFields: [
|
|
248
|
+
"account",
|
|
249
|
+
"configPath",
|
|
250
|
+
"httpUrl",
|
|
251
|
+
"httpHost",
|
|
252
|
+
"httpPort",
|
|
253
|
+
"cliPath",
|
|
254
|
+
"name"
|
|
255
|
+
],
|
|
256
|
+
resolveAllowFrom: (account) => account.config.allowFrom,
|
|
257
|
+
formatAllowFrom: (allowFrom) => normalizeStringifiedEntries(allowFrom).map((entry) => entry === "*" ? "*" : normalizeE164(entry.replace(/^signal:/i, ""))).filter(Boolean),
|
|
258
|
+
resolveDefaultTo: (account) => account.config.defaultTo
|
|
259
|
+
});
|
|
260
|
+
const signalSecurityAdapter = createRestrictSendersChannelSecurity({
|
|
261
|
+
channelKey: SIGNAL_CHANNEL,
|
|
262
|
+
resolveDmPolicy: (account) => account.config.dmPolicy,
|
|
263
|
+
resolveDmAllowFrom: (account) => account.config.allowFrom,
|
|
264
|
+
resolveGroupPolicy: (account) => account.config.groupPolicy,
|
|
265
|
+
surface: "Signal groups",
|
|
266
|
+
openScope: "any member",
|
|
267
|
+
groupPolicyPath: "channels.signal.groupPolicy",
|
|
268
|
+
groupAllowFromPath: "channels.signal.groupAllowFrom",
|
|
269
|
+
mentionGated: false,
|
|
270
|
+
policyPathSuffix: "dmPolicy",
|
|
271
|
+
normalizeDmEntry: (raw) => normalizeE164(raw.replace(/^signal:/i, "").trim())
|
|
272
|
+
});
|
|
273
|
+
function createSignalPluginBase(params) {
|
|
274
|
+
return {
|
|
275
|
+
...createChannelPluginBase({
|
|
276
|
+
id: SIGNAL_CHANNEL,
|
|
277
|
+
meta: { ...getChatChannelMeta(SIGNAL_CHANNEL) },
|
|
278
|
+
setupWizard: params.setupWizard,
|
|
279
|
+
capabilities: {
|
|
280
|
+
chatTypes: ["direct", "group"],
|
|
281
|
+
media: true,
|
|
282
|
+
reactions: true
|
|
283
|
+
},
|
|
284
|
+
streaming: { blockStreamingCoalesceDefaults: {
|
|
285
|
+
minChars: 1500,
|
|
286
|
+
idleMs: 1e3
|
|
287
|
+
} },
|
|
288
|
+
reload: { configPrefixes: ["channels.signal"] },
|
|
289
|
+
configSchema: SignalChannelConfigSchema,
|
|
290
|
+
config: {
|
|
291
|
+
...signalConfigAdapter,
|
|
292
|
+
isConfigured: (account) => account.configured,
|
|
293
|
+
describeAccount: (account) => describeAccountSnapshot({
|
|
294
|
+
account,
|
|
295
|
+
configured: account.configured,
|
|
296
|
+
extra: { baseUrl: account.baseUrl }
|
|
297
|
+
})
|
|
298
|
+
},
|
|
299
|
+
security: signalSecurityAdapter,
|
|
300
|
+
setup: params.setup
|
|
301
|
+
}),
|
|
302
|
+
messaging: { defaultMarkdownTableMode: "bullets" }
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region extensions/signal/src/channel.ts
|
|
307
|
+
let signalMonitorModulePromise = null;
|
|
308
|
+
let signalProbeModulePromise = null;
|
|
309
|
+
let signalSendRuntimePromise = null;
|
|
310
|
+
async function loadSignalMonitorModule() {
|
|
311
|
+
signalMonitorModulePromise ??= import("./monitor-CUhIKHJo.js").then((n) => n.n);
|
|
312
|
+
return await signalMonitorModulePromise;
|
|
313
|
+
}
|
|
314
|
+
async function loadSignalProbeModule() {
|
|
315
|
+
signalProbeModulePromise ??= import("./probe-BQ_Izoya.js").then((n) => n.n);
|
|
316
|
+
return await signalProbeModulePromise;
|
|
317
|
+
}
|
|
318
|
+
async function loadSignalSendRuntime() {
|
|
319
|
+
signalSendRuntimePromise ??= import("./send.runtime-CfaZd8X4.js");
|
|
320
|
+
return await signalSendRuntimePromise;
|
|
321
|
+
}
|
|
322
|
+
async function resolveSignalSendContext(params) {
|
|
323
|
+
return {
|
|
324
|
+
send: resolveOutboundSendDep(params.deps, "signal") ?? (await loadSignalSendRuntime()).sendMessageSignal,
|
|
325
|
+
maxBytes: resolveChannelMediaMaxBytes({
|
|
326
|
+
cfg: params.cfg,
|
|
327
|
+
resolveChannelLimitMb: ({ cfg, accountId }) => cfg.channels?.signal?.accounts?.[accountId]?.mediaMaxMb ?? cfg.channels?.signal?.mediaMaxMb,
|
|
328
|
+
accountId: params.accountId
|
|
329
|
+
})
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
async function sendSignalOutbound(params) {
|
|
333
|
+
const { send, maxBytes } = await resolveSignalSendContext(params);
|
|
334
|
+
return await send(params.to, params.text, {
|
|
335
|
+
cfg: params.cfg,
|
|
336
|
+
...params.mediaUrl ? { mediaUrl: params.mediaUrl } : {},
|
|
337
|
+
...params.mediaLocalRoots?.length ? { mediaLocalRoots: params.mediaLocalRoots } : {},
|
|
338
|
+
...params.mediaReadFile ? { mediaReadFile: params.mediaReadFile } : {},
|
|
339
|
+
maxBytes,
|
|
340
|
+
accountId: params.accountId ?? void 0
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const signalMessageAdapter = defineChannelMessageAdapter({
|
|
344
|
+
id: "signal",
|
|
345
|
+
durableFinal: { capabilities: {
|
|
346
|
+
text: true,
|
|
347
|
+
media: true
|
|
348
|
+
} },
|
|
349
|
+
send: {
|
|
350
|
+
text: async (ctx) => await sendSignalOutbound({
|
|
351
|
+
cfg: ctx.cfg,
|
|
352
|
+
to: ctx.to,
|
|
353
|
+
text: ctx.text,
|
|
354
|
+
accountId: ctx.accountId ?? void 0,
|
|
355
|
+
deps: ctx.deps
|
|
356
|
+
}),
|
|
357
|
+
media: async (ctx) => await sendSignalOutbound({
|
|
358
|
+
cfg: ctx.cfg,
|
|
359
|
+
to: ctx.to,
|
|
360
|
+
text: ctx.text,
|
|
361
|
+
mediaUrl: ctx.mediaUrl,
|
|
362
|
+
mediaLocalRoots: ctx.mediaLocalRoots,
|
|
363
|
+
mediaReadFile: ctx.mediaReadFile,
|
|
364
|
+
accountId: ctx.accountId ?? void 0,
|
|
365
|
+
deps: ctx.deps
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
function inferSignalTargetChatType(rawTo) {
|
|
370
|
+
let to = rawTo.trim();
|
|
371
|
+
if (!to) return;
|
|
372
|
+
if (/^signal:/i.test(to)) to = to.replace(/^signal:/i, "").trim();
|
|
373
|
+
if (!to) return;
|
|
374
|
+
const lower = normalizeLowercaseStringOrEmpty(to);
|
|
375
|
+
if (lower.startsWith("group:")) return "group";
|
|
376
|
+
if (lower.startsWith("username:") || lower.startsWith("u:")) return "direct";
|
|
377
|
+
return "direct";
|
|
378
|
+
}
|
|
379
|
+
function buildSignalBaseSessionKey(params) {
|
|
380
|
+
return buildOutboundBaseSessionKey({
|
|
381
|
+
...params,
|
|
382
|
+
channel: "signal"
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
function resolveSignalOutboundSessionRoute(params) {
|
|
386
|
+
const resolved = resolveSignalOutboundTarget(params.target);
|
|
387
|
+
if (!resolved) return null;
|
|
388
|
+
const baseSessionKey = buildSignalBaseSessionKey({
|
|
389
|
+
cfg: params.cfg,
|
|
390
|
+
agentId: params.agentId,
|
|
391
|
+
accountId: params.accountId,
|
|
392
|
+
peer: resolved.peer
|
|
393
|
+
});
|
|
394
|
+
return {
|
|
395
|
+
sessionKey: baseSessionKey,
|
|
396
|
+
baseSessionKey,
|
|
397
|
+
...resolved
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
async function sendFormattedSignalText(ctx) {
|
|
401
|
+
const { send, maxBytes } = await resolveSignalSendContext({
|
|
402
|
+
cfg: ctx.cfg,
|
|
403
|
+
accountId: ctx.accountId ?? void 0,
|
|
404
|
+
deps: ctx.deps
|
|
405
|
+
});
|
|
406
|
+
const limit = resolveTextChunkLimit(ctx.cfg, "signal", ctx.accountId ?? void 0, { fallbackLimit: 4e3 });
|
|
407
|
+
const tableMode = resolveMarkdownTableMode({
|
|
408
|
+
cfg: ctx.cfg,
|
|
409
|
+
channel: "signal",
|
|
410
|
+
accountId: ctx.accountId ?? void 0
|
|
411
|
+
});
|
|
412
|
+
let chunks = limit === void 0 ? markdownToSignalTextChunks(ctx.text, Number.POSITIVE_INFINITY, { tableMode }) : markdownToSignalTextChunks(ctx.text, limit, { tableMode });
|
|
413
|
+
if (chunks.length === 0 && ctx.text) chunks = [{
|
|
414
|
+
text: ctx.text,
|
|
415
|
+
styles: []
|
|
416
|
+
}];
|
|
417
|
+
const results = [];
|
|
418
|
+
for (const chunk of chunks) {
|
|
419
|
+
ctx.abortSignal?.throwIfAborted();
|
|
420
|
+
const result = await send(ctx.to, chunk.text, {
|
|
421
|
+
cfg: ctx.cfg,
|
|
422
|
+
maxBytes,
|
|
423
|
+
accountId: ctx.accountId ?? void 0,
|
|
424
|
+
textMode: "plain",
|
|
425
|
+
textStyles: chunk.styles
|
|
426
|
+
});
|
|
427
|
+
results.push(result);
|
|
428
|
+
}
|
|
429
|
+
return attachChannelToResults("signal", results);
|
|
430
|
+
}
|
|
431
|
+
async function sendFormattedSignalMedia(ctx) {
|
|
432
|
+
ctx.abortSignal?.throwIfAborted();
|
|
433
|
+
const { send, maxBytes } = await resolveSignalSendContext({
|
|
434
|
+
cfg: ctx.cfg,
|
|
435
|
+
accountId: ctx.accountId ?? void 0,
|
|
436
|
+
deps: ctx.deps
|
|
437
|
+
});
|
|
438
|
+
const tableMode = resolveMarkdownTableMode({
|
|
439
|
+
cfg: ctx.cfg,
|
|
440
|
+
channel: "signal",
|
|
441
|
+
accountId: ctx.accountId ?? void 0
|
|
442
|
+
});
|
|
443
|
+
const formatted = markdownToSignalTextChunks(ctx.text, Number.POSITIVE_INFINITY, { tableMode })[0] ?? {
|
|
444
|
+
text: ctx.text,
|
|
445
|
+
styles: []
|
|
446
|
+
};
|
|
447
|
+
return attachChannelToResult("signal", await send(ctx.to, formatted.text, {
|
|
448
|
+
cfg: ctx.cfg,
|
|
449
|
+
mediaUrl: ctx.mediaUrl,
|
|
450
|
+
mediaLocalRoots: ctx.mediaLocalRoots,
|
|
451
|
+
...ctx.mediaReadFile ? { mediaReadFile: ctx.mediaReadFile } : {},
|
|
452
|
+
maxBytes,
|
|
453
|
+
accountId: ctx.accountId ?? void 0,
|
|
454
|
+
textMode: "plain",
|
|
455
|
+
textStyles: formatted.styles
|
|
456
|
+
}));
|
|
457
|
+
}
|
|
458
|
+
const signalPlugin = createChatChannelPlugin({
|
|
459
|
+
base: {
|
|
460
|
+
...createSignalPluginBase({
|
|
461
|
+
setupWizard: signalSetupWizard,
|
|
462
|
+
setup: signalSetupAdapter
|
|
463
|
+
}),
|
|
464
|
+
actions: signalMessageActions,
|
|
465
|
+
approvalCapability: signalApprovalCapability,
|
|
466
|
+
allowlist: buildDmGroupAccountAllowlistAdapter({
|
|
467
|
+
channelId: "signal",
|
|
468
|
+
resolveAccount: resolveSignalAccount,
|
|
469
|
+
normalize: ({ cfg, accountId, values }) => signalConfigAdapter.formatAllowFrom({
|
|
470
|
+
cfg,
|
|
471
|
+
accountId,
|
|
472
|
+
allowFrom: values
|
|
473
|
+
}),
|
|
474
|
+
resolveDmAllowFrom: (account) => account.config.allowFrom,
|
|
475
|
+
resolveGroupAllowFrom: (account) => account.config.groupAllowFrom,
|
|
476
|
+
resolveDmPolicy: (account) => account.config.dmPolicy,
|
|
477
|
+
resolveGroupPolicy: (account) => account.config.groupPolicy
|
|
478
|
+
}),
|
|
479
|
+
agentPrompt: { reactionGuidance: ({ cfg, accountId }) => {
|
|
480
|
+
const level = resolveSignalReactionLevel({
|
|
481
|
+
cfg,
|
|
482
|
+
accountId: accountId ?? void 0
|
|
483
|
+
}).agentReactionGuidance;
|
|
484
|
+
return level ? {
|
|
485
|
+
level,
|
|
486
|
+
channelLabel: "Signal"
|
|
487
|
+
} : void 0;
|
|
488
|
+
} },
|
|
489
|
+
messaging: {
|
|
490
|
+
targetPrefixes: ["signal"],
|
|
491
|
+
normalizeTarget: normalizeSignalMessagingTarget,
|
|
492
|
+
inferTargetChatType: ({ to }) => inferSignalTargetChatType(to),
|
|
493
|
+
resolveOutboundSessionRoute: (params) => resolveSignalOutboundSessionRoute(params),
|
|
494
|
+
targetResolver: {
|
|
495
|
+
looksLikeId: looksLikeSignalTargetId,
|
|
496
|
+
hint: "<E.164|uuid:ID|group:ID|signal:group:ID|signal:+E.164>"
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
heartbeat: {
|
|
500
|
+
sendTyping: async ({ cfg, to, accountId }) => {
|
|
501
|
+
await (await loadSignalSendRuntime()).sendTypingSignal(to, {
|
|
502
|
+
cfg,
|
|
503
|
+
...accountId ? { accountId } : {}
|
|
504
|
+
});
|
|
505
|
+
},
|
|
506
|
+
clearTyping: async ({ cfg, to, accountId }) => {
|
|
507
|
+
await (await loadSignalSendRuntime()).sendTypingSignal(to, {
|
|
508
|
+
cfg,
|
|
509
|
+
...accountId ? { accountId } : {},
|
|
510
|
+
stop: true
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
status: createComputedAccountStatusAdapter({
|
|
515
|
+
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
|
|
516
|
+
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("signal", accounts),
|
|
517
|
+
buildChannelSummary: ({ snapshot }) => buildBaseChannelStatusSummary(snapshot, {
|
|
518
|
+
baseUrl: snapshot.baseUrl ?? null,
|
|
519
|
+
probe: snapshot.probe,
|
|
520
|
+
lastProbeAt: snapshot.lastProbeAt ?? null
|
|
521
|
+
}),
|
|
522
|
+
probeAccount: async ({ account, timeoutMs }) => {
|
|
523
|
+
const baseUrl = account.baseUrl;
|
|
524
|
+
const { probeSignal } = await loadSignalProbeModule();
|
|
525
|
+
return await probeSignal(baseUrl, timeoutMs, { apiMode: account.config?.apiMode ?? "auto" });
|
|
526
|
+
},
|
|
527
|
+
formatCapabilitiesProbe: ({ probe }) => probe?.version ? [{ text: `Signal daemon: ${probe.version}` }] : [],
|
|
528
|
+
resolveAccountSnapshot: ({ account }) => ({
|
|
529
|
+
accountId: account.accountId,
|
|
530
|
+
name: account.name,
|
|
531
|
+
enabled: account.enabled,
|
|
532
|
+
configured: account.configured,
|
|
533
|
+
extra: { baseUrl: account.baseUrl }
|
|
534
|
+
})
|
|
535
|
+
}),
|
|
536
|
+
gateway: { startAccount: async (ctx) => {
|
|
537
|
+
const account = ctx.account;
|
|
538
|
+
ctx.setStatus({
|
|
539
|
+
accountId: account.accountId,
|
|
540
|
+
baseUrl: account.baseUrl
|
|
541
|
+
});
|
|
542
|
+
ctx.log?.info(`[${account.accountId}] starting provider (${account.baseUrl})`);
|
|
543
|
+
const { monitorSignalProvider } = await loadSignalMonitorModule();
|
|
544
|
+
return await monitorSignalProvider({
|
|
545
|
+
accountId: account.accountId,
|
|
546
|
+
config: ctx.cfg,
|
|
547
|
+
runtime: ctx.runtime,
|
|
548
|
+
channelRuntime: ctx.channelRuntime,
|
|
549
|
+
abortSignal: ctx.abortSignal,
|
|
550
|
+
mediaMaxMb: account.config.mediaMaxMb
|
|
551
|
+
});
|
|
552
|
+
} },
|
|
553
|
+
message: signalMessageAdapter
|
|
554
|
+
},
|
|
555
|
+
pairing: { text: {
|
|
556
|
+
idLabel: "signalNumber",
|
|
557
|
+
message: PAIRING_APPROVED_MESSAGE,
|
|
558
|
+
normalizeAllowEntry: createPairingPrefixStripper(/^signal:/i),
|
|
559
|
+
notify: async ({ cfg, id, message }) => {
|
|
560
|
+
await (await loadSignalSendRuntime()).sendMessageSignal(id, message, { cfg });
|
|
561
|
+
}
|
|
562
|
+
} },
|
|
563
|
+
security: signalSecurityAdapter,
|
|
564
|
+
outbound: {
|
|
565
|
+
base: {
|
|
566
|
+
deliveryMode: "direct",
|
|
567
|
+
chunker: chunkText,
|
|
568
|
+
chunkerMode: "text",
|
|
569
|
+
textChunkLimit: 4e3,
|
|
570
|
+
shouldSuppressLocalPayloadPrompt: ({ cfg, accountId, payload, hint }) => shouldSuppressLocalSignalExecApprovalPrompt({
|
|
571
|
+
cfg,
|
|
572
|
+
accountId,
|
|
573
|
+
payload,
|
|
574
|
+
hint
|
|
575
|
+
}),
|
|
576
|
+
sendFormattedText: async ({ cfg, to, text, accountId, deps, abortSignal }) => await sendFormattedSignalText({
|
|
577
|
+
cfg,
|
|
578
|
+
to,
|
|
579
|
+
text,
|
|
580
|
+
accountId,
|
|
581
|
+
deps,
|
|
582
|
+
abortSignal
|
|
583
|
+
}),
|
|
584
|
+
sendFormattedMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, mediaReadFile, accountId, deps, abortSignal }) => await sendFormattedSignalMedia({
|
|
585
|
+
cfg,
|
|
586
|
+
to,
|
|
587
|
+
text,
|
|
588
|
+
mediaUrl,
|
|
589
|
+
mediaLocalRoots,
|
|
590
|
+
mediaReadFile,
|
|
591
|
+
accountId,
|
|
592
|
+
deps,
|
|
593
|
+
abortSignal
|
|
594
|
+
})
|
|
595
|
+
},
|
|
596
|
+
attachedResults: {
|
|
597
|
+
channel: "signal",
|
|
598
|
+
sendText: async ({ cfg, to, text, accountId, deps }) => await sendSignalOutbound({
|
|
599
|
+
cfg,
|
|
600
|
+
to,
|
|
601
|
+
text,
|
|
602
|
+
accountId: accountId ?? void 0,
|
|
603
|
+
deps
|
|
604
|
+
}),
|
|
605
|
+
sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, mediaReadFile, accountId, deps }) => await sendSignalOutbound({
|
|
606
|
+
cfg,
|
|
607
|
+
to,
|
|
608
|
+
text,
|
|
609
|
+
mediaUrl,
|
|
610
|
+
mediaLocalRoots,
|
|
611
|
+
mediaReadFile,
|
|
612
|
+
accountId: accountId ?? void 0,
|
|
613
|
+
deps
|
|
614
|
+
})
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
//#endregion
|
|
619
|
+
export { normalizeSignalAccountInput as a, signalNumberTextInput as c, createSignalCliPathTextInput as i, signalSetupAdapter as l, createSignalPluginBase as n, signalCompletionNote as o, signalSetupWizard as r, signalDmPolicy as s, signalPlugin as t, resolveSignalOutboundTarget as u };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
//#region extensions/signal/channel-entry.ts
|
|
3
|
+
var channel_entry_default = defineBundledChannelEntry({
|
|
4
|
+
id: "signal",
|
|
5
|
+
name: "Signal",
|
|
6
|
+
description: "Signal channel plugin",
|
|
7
|
+
importMetaUrl: import.meta.url,
|
|
8
|
+
plugin: {
|
|
9
|
+
specifier: "./api.js",
|
|
10
|
+
exportName: "signalPlugin"
|
|
11
|
+
},
|
|
12
|
+
runtime: {
|
|
13
|
+
specifier: "./runtime-api.js",
|
|
14
|
+
exportName: "setSignalRuntime"
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
export { channel_entry_default as default };
|