cclawd 1.0.2 → 1.0.4

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 (68) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/plugin-sdk/active-listener-CN-tMEvN.js +35 -0
  3. package/dist/plugin-sdk/api-key-rotation-CimGYMBc.js +176 -0
  4. package/dist/plugin-sdk/audio-preflight-C-xXBoE2.js +51 -0
  5. package/dist/plugin-sdk/audio-transcription-runner-CTIHpebA.js +2173 -0
  6. package/dist/plugin-sdk/audit-membership-runtime-BFatB2LJ.js +58 -0
  7. package/dist/plugin-sdk/channel-activity-DO0FEzyj.js +95 -0
  8. package/dist/plugin-sdk/channel-web-Da-__nUF.js +2238 -0
  9. package/dist/plugin-sdk/commands-registry-6no2NNrY.js +1118 -0
  10. package/dist/plugin-sdk/compact.runtime-CCoclu5e.js +35 -0
  11. package/dist/plugin-sdk/config-B9ODwgpz.js +37426 -0
  12. package/dist/plugin-sdk/deliver-B1fFpKjV.js +1757 -0
  13. package/dist/plugin-sdk/deliver-runtime-DB-VRMe1.js +15 -0
  14. package/dist/plugin-sdk/deps-send-discord.runtime-DklqycYG.js +15 -0
  15. package/dist/plugin-sdk/deps-send-imessage.runtime-Chs8zeon.js +14 -0
  16. package/dist/plugin-sdk/deps-send-signal.runtime-clW9aSJP.js +13 -0
  17. package/dist/plugin-sdk/deps-send-slack.runtime-BUx0LYY1.js +13 -0
  18. package/dist/plugin-sdk/deps-send-telegram.runtime-LECSHgMG.js +16 -0
  19. package/dist/plugin-sdk/deps-send-whatsapp.runtime-D2d65fw0.js +40 -0
  20. package/dist/plugin-sdk/diagnostic-CxIvS-C2.js +315 -0
  21. package/dist/plugin-sdk/dispatch-BqlR4dPx.js +105863 -0
  22. package/dist/plugin-sdk/env-b9k1PHMI.js +34 -0
  23. package/dist/plugin-sdk/fetch-PoxzAANT.js +326 -0
  24. package/dist/plugin-sdk/fetch-guard-4UVSZ0uS.js +164 -0
  25. package/dist/plugin-sdk/image-Ch6M4tnJ.js +2420 -0
  26. package/dist/plugin-sdk/image-runtime-CSh2o5wY.js +8 -0
  27. package/dist/plugin-sdk/index.js +35 -35
  28. package/dist/plugin-sdk/ir-CugsqGH8.js +1312 -0
  29. package/dist/plugin-sdk/local-roots-adnEg9zb.js +217 -0
  30. package/dist/plugin-sdk/logger-D6zRubj0.js +1164 -0
  31. package/dist/plugin-sdk/login-CYvkQ0At.js +54 -0
  32. package/dist/plugin-sdk/login-qr-ll4NtaT5.js +316 -0
  33. package/dist/plugin-sdk/manager-CHy8IclH.js +3959 -0
  34. package/dist/plugin-sdk/manager-runtime-C70EkEr7.js +11 -0
  35. package/dist/plugin-sdk/outbound-Wzs2iN7X.js +216 -0
  36. package/dist/plugin-sdk/outbound-attachment-khXJwucX.js +17 -0
  37. package/dist/plugin-sdk/paths-BtVqCdw4.js +3063 -0
  38. package/dist/plugin-sdk/pi-model-discovery-Dh4ziodY.js +131 -0
  39. package/dist/plugin-sdk/pi-model-discovery-runtime-b83Xe-HT.js +8 -0
  40. package/dist/plugin-sdk/pi-tools.before-tool-call.runtime-C1z5CDBF.js +349 -0
  41. package/dist/plugin-sdk/proxy-fetch-CJEmoBxi.js +54 -0
  42. package/dist/plugin-sdk/pw-ai-Dj3Cvlzl.js +1990 -0
  43. package/dist/plugin-sdk/qmd-manager-egHUAseQ.js +1581 -0
  44. package/dist/plugin-sdk/resolve-outbound-target-BiICvIKs.js +38 -0
  45. package/dist/plugin-sdk/runtime-whatsapp-login.runtime-DNApufzW.js +9 -0
  46. package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-CBmtfIQ8.js +13 -0
  47. package/dist/plugin-sdk/send-CScblaI4.js +532 -0
  48. package/dist/plugin-sdk/send-CeHhnld6.js +407 -0
  49. package/dist/plugin-sdk/send-DP_c8JfR.js +3277 -0
  50. package/dist/plugin-sdk/send-Dc5fI6e8.js +495 -0
  51. package/dist/plugin-sdk/send-l-77_s1_.js +2507 -0
  52. package/dist/plugin-sdk/session-CkOKZaqa.js +166 -0
  53. package/dist/plugin-sdk/signal.js +2 -2
  54. package/dist/plugin-sdk/skill-commands-BohYCgkq.js +336 -0
  55. package/dist/plugin-sdk/slash-commands.runtime-DpLfVTM6.js +8 -0
  56. package/dist/plugin-sdk/slash-dispatch.runtime-CASMHwpm.js +35 -0
  57. package/dist/plugin-sdk/slash-skill-commands.runtime-D7rrJEci.js +9 -0
  58. package/dist/plugin-sdk/sqlite-CJE3X7Mv.js +1005 -0
  59. package/dist/plugin-sdk/subagent-registry-runtime-B1oo5bih.js +35 -0
  60. package/dist/plugin-sdk/tables-D5VgpTmm.js +53 -0
  61. package/dist/plugin-sdk/target-errors-C6zZ_OpA.js +191 -0
  62. package/dist/plugin-sdk/tokens-DUnJnpMS.js +50 -0
  63. package/dist/plugin-sdk/web-TfUM1nSi.js +39 -0
  64. package/dist/plugin-sdk/whatsapp-actions-DuWJ0j1r.js +71 -0
  65. package/extensions/mfa-auth/index.ts +36 -17
  66. package/extensions/mfa-auth/src/auth-manager.ts +4 -0
  67. package/extensions/mfa-auth/src/notification-service.ts +5 -1
  68. package/package.json +1 -1
@@ -0,0 +1,38 @@
1
+ import { do as normalizeWhatsAppTarget, uo as isWhatsAppGroupJid } from "./config-B9ODwgpz.js";
2
+ import { n as missingTargetError } from "./target-errors-C6zZ_OpA.js";
3
+ //#region src/whatsapp/resolve-outbound-target.ts
4
+ function resolveWhatsAppOutboundTarget(params) {
5
+ const trimmed = params.to?.trim() ?? "";
6
+ const allowListRaw = (params.allowFrom ?? []).map((entry) => String(entry).trim()).filter(Boolean);
7
+ const hasWildcard = allowListRaw.includes("*");
8
+ const allowList = allowListRaw.filter((entry) => entry !== "*").map((entry) => normalizeWhatsAppTarget(entry)).filter((entry) => Boolean(entry));
9
+ if (trimmed) {
10
+ const normalizedTo = normalizeWhatsAppTarget(trimmed);
11
+ if (!normalizedTo) return {
12
+ ok: false,
13
+ error: missingTargetError("WhatsApp", "<E.164|group JID>")
14
+ };
15
+ if (isWhatsAppGroupJid(normalizedTo)) return {
16
+ ok: true,
17
+ to: normalizedTo
18
+ };
19
+ if (hasWildcard || allowList.length === 0) return {
20
+ ok: true,
21
+ to: normalizedTo
22
+ };
23
+ if (allowList.includes(normalizedTo)) return {
24
+ ok: true,
25
+ to: normalizedTo
26
+ };
27
+ return {
28
+ ok: false,
29
+ error: missingTargetError("WhatsApp", "<E.164|group JID>")
30
+ };
31
+ }
32
+ return {
33
+ ok: false,
34
+ error: missingTargetError("WhatsApp", "<E.164|group JID>")
35
+ };
36
+ }
37
+ //#endregion
38
+ export { resolveWhatsAppOutboundTarget as t };
@@ -0,0 +1,9 @@
1
+ import "./paths-BtVqCdw4.js";
2
+ import "./config-B9ODwgpz.js";
3
+ import "./paths-eFexkPEh.js";
4
+ import "./github-copilot-token-Cxf8QYZb.js";
5
+ import "./logger-D6zRubj0.js";
6
+ import "./env-b9k1PHMI.js";
7
+ import "./session-CkOKZaqa.js";
8
+ import { t as loginWeb } from "./login-CYvkQ0At.js";
9
+ export { loginWeb };
@@ -0,0 +1,13 @@
1
+ import "./paths-BtVqCdw4.js";
2
+ import "./config-B9ODwgpz.js";
3
+ import "./paths-eFexkPEh.js";
4
+ import "./github-copilot-token-Cxf8QYZb.js";
5
+ import "./logger-D6zRubj0.js";
6
+ import "./env-b9k1PHMI.js";
7
+ import "./fetch-guard-4UVSZ0uS.js";
8
+ import "./local-roots-adnEg9zb.js";
9
+ import "./ir-CugsqGH8.js";
10
+ import "./render-CKf6NJ1M.js";
11
+ import "./tables-D5VgpTmm.js";
12
+ import { n as sendPollWhatsApp, t as sendMessageWhatsApp } from "./outbound-Wzs2iN7X.js";
13
+ export { sendMessageWhatsApp, sendPollWhatsApp };
@@ -0,0 +1,532 @@
1
+ import { Da as resolveSignalAccount, js as kindFromMime, r as loadConfig, ts as generateSecureUuid } from "./config-B9ODwgpz.js";
2
+ import { n as fetchWithTimeout } from "./fetch-timeout-D2j7yeE1.js";
3
+ import { t as resolveFetch } from "./fetch-BSKpf2dM.js";
4
+ import { i as resolveMarkdownTableMode, n as markdownToIR, t as chunkMarkdownIR } from "./ir-CugsqGH8.js";
5
+ import { t as resolveOutboundAttachmentFromUrl } from "./outbound-attachment-khXJwucX.js";
6
+ //#region src/signal/format.ts
7
+ function normalizeUrlForComparison(url) {
8
+ let normalized = url.toLowerCase();
9
+ normalized = normalized.replace(/^https?:\/\//, "");
10
+ normalized = normalized.replace(/^www\./, "");
11
+ normalized = normalized.replace(/\/+$/, "");
12
+ return normalized;
13
+ }
14
+ function mapStyle(style) {
15
+ switch (style) {
16
+ case "bold": return "BOLD";
17
+ case "italic": return "ITALIC";
18
+ case "strikethrough": return "STRIKETHROUGH";
19
+ case "code":
20
+ case "code_block": return "MONOSPACE";
21
+ case "spoiler": return "SPOILER";
22
+ default: return null;
23
+ }
24
+ }
25
+ function mergeStyles(styles) {
26
+ const sorted = [...styles].toSorted((a, b) => {
27
+ if (a.start !== b.start) return a.start - b.start;
28
+ if (a.length !== b.length) return a.length - b.length;
29
+ return a.style.localeCompare(b.style);
30
+ });
31
+ const merged = [];
32
+ for (const style of sorted) {
33
+ const prev = merged[merged.length - 1];
34
+ if (prev && prev.style === style.style && style.start <= prev.start + prev.length) {
35
+ const prevEnd = prev.start + prev.length;
36
+ prev.length = Math.max(prevEnd, style.start + style.length) - prev.start;
37
+ continue;
38
+ }
39
+ merged.push({ ...style });
40
+ }
41
+ return merged;
42
+ }
43
+ function clampStyles(styles, maxLength) {
44
+ const clamped = [];
45
+ for (const style of styles) {
46
+ const start = Math.max(0, Math.min(style.start, maxLength));
47
+ const length = Math.min(style.start + style.length, maxLength) - start;
48
+ if (length > 0) clamped.push({
49
+ start,
50
+ length,
51
+ style: style.style
52
+ });
53
+ }
54
+ return clamped;
55
+ }
56
+ function applyInsertionsToStyles(spans, insertions) {
57
+ if (insertions.length === 0) return spans;
58
+ const sortedInsertions = [...insertions].toSorted((a, b) => a.pos - b.pos);
59
+ let updated = spans;
60
+ let cumulativeShift = 0;
61
+ for (const insertion of sortedInsertions) {
62
+ const insertionPos = insertion.pos + cumulativeShift;
63
+ const next = [];
64
+ for (const span of updated) {
65
+ if (span.end <= insertionPos) {
66
+ next.push(span);
67
+ continue;
68
+ }
69
+ if (span.start >= insertionPos) {
70
+ next.push({
71
+ start: span.start + insertion.length,
72
+ end: span.end + insertion.length,
73
+ style: span.style
74
+ });
75
+ continue;
76
+ }
77
+ if (span.start < insertionPos && span.end > insertionPos) {
78
+ if (insertionPos > span.start) next.push({
79
+ start: span.start,
80
+ end: insertionPos,
81
+ style: span.style
82
+ });
83
+ const shiftedStart = insertionPos + insertion.length;
84
+ const shiftedEnd = span.end + insertion.length;
85
+ if (shiftedEnd > shiftedStart) next.push({
86
+ start: shiftedStart,
87
+ end: shiftedEnd,
88
+ style: span.style
89
+ });
90
+ }
91
+ }
92
+ updated = next;
93
+ cumulativeShift += insertion.length;
94
+ }
95
+ return updated;
96
+ }
97
+ function renderSignalText(ir) {
98
+ const text = ir.text ?? "";
99
+ if (!text) return {
100
+ text: "",
101
+ styles: []
102
+ };
103
+ const sortedLinks = [...ir.links].toSorted((a, b) => a.start - b.start);
104
+ let out = "";
105
+ let cursor = 0;
106
+ const insertions = [];
107
+ for (const link of sortedLinks) {
108
+ if (link.start < cursor) continue;
109
+ out += text.slice(cursor, link.end);
110
+ const href = link.href.trim();
111
+ const trimmedLabel = text.slice(link.start, link.end).trim();
112
+ if (href) if (!trimmedLabel) {
113
+ out += href;
114
+ insertions.push({
115
+ pos: link.end,
116
+ length: href.length
117
+ });
118
+ } else {
119
+ const normalizedLabel = normalizeUrlForComparison(trimmedLabel);
120
+ let comparableHref = href;
121
+ if (href.startsWith("mailto:")) comparableHref = href.slice(7);
122
+ if (normalizedLabel !== normalizeUrlForComparison(comparableHref)) {
123
+ const addition = ` (${href})`;
124
+ out += addition;
125
+ insertions.push({
126
+ pos: link.end,
127
+ length: addition.length
128
+ });
129
+ }
130
+ }
131
+ cursor = link.end;
132
+ }
133
+ out += text.slice(cursor);
134
+ const adjusted = applyInsertionsToStyles(ir.styles.map((span) => {
135
+ const mapped = mapStyle(span.style);
136
+ if (!mapped) return null;
137
+ return {
138
+ start: span.start,
139
+ end: span.end,
140
+ style: mapped
141
+ };
142
+ }).filter((span) => span !== null), insertions);
143
+ const trimmedText = out.trimEnd();
144
+ const trimmedLength = trimmedText.length;
145
+ return {
146
+ text: trimmedText,
147
+ styles: mergeStyles(clampStyles(adjusted.map((span) => ({
148
+ start: span.start,
149
+ length: span.end - span.start,
150
+ style: span.style
151
+ })), trimmedLength))
152
+ };
153
+ }
154
+ function markdownToSignalText(markdown, options = {}) {
155
+ return renderSignalText(markdownToIR(markdown ?? "", {
156
+ linkify: true,
157
+ enableSpoilers: true,
158
+ headingStyle: "bold",
159
+ blockquotePrefix: "> ",
160
+ tableMode: options.tableMode
161
+ }));
162
+ }
163
+ function sliceSignalStyles(styles, start, end) {
164
+ const sliced = [];
165
+ for (const style of styles) {
166
+ const styleEnd = style.start + style.length;
167
+ const sliceStart = Math.max(style.start, start);
168
+ const sliceEnd = Math.min(styleEnd, end);
169
+ if (sliceEnd > sliceStart) sliced.push({
170
+ start: sliceStart - start,
171
+ length: sliceEnd - sliceStart,
172
+ style: style.style
173
+ });
174
+ }
175
+ return sliced;
176
+ }
177
+ /**
178
+ * Split Signal formatted text into chunks under the limit while preserving styles.
179
+ *
180
+ * This implementation deterministically tracks cursor position without using indexOf,
181
+ * which is fragile when chunks are trimmed or when duplicate substrings exist.
182
+ * Styles spanning chunk boundaries are split into separate ranges for each chunk.
183
+ */
184
+ function splitSignalFormattedText(formatted, limit) {
185
+ const { text, styles } = formatted;
186
+ if (text.length <= limit) return [formatted];
187
+ const results = [];
188
+ let remaining = text;
189
+ let offset = 0;
190
+ while (remaining.length > 0) {
191
+ if (remaining.length <= limit) {
192
+ const trimmed = remaining.trimEnd();
193
+ if (trimmed.length > 0) results.push({
194
+ text: trimmed,
195
+ styles: mergeStyles(sliceSignalStyles(styles, offset, offset + trimmed.length))
196
+ });
197
+ break;
198
+ }
199
+ let breakIdx = findBreakIndex(remaining.slice(0, limit));
200
+ if (breakIdx <= 0) breakIdx = limit;
201
+ const chunk = remaining.slice(0, breakIdx).trimEnd();
202
+ if (chunk.length > 0) results.push({
203
+ text: chunk,
204
+ styles: mergeStyles(sliceSignalStyles(styles, offset, offset + chunk.length))
205
+ });
206
+ const brokeOnWhitespace = breakIdx < remaining.length && /\s/.test(remaining[breakIdx]);
207
+ const nextStart = Math.min(remaining.length, breakIdx + (brokeOnWhitespace ? 1 : 0));
208
+ remaining = remaining.slice(nextStart).trimStart();
209
+ offset = text.length - remaining.length;
210
+ }
211
+ return results;
212
+ }
213
+ /**
214
+ * Find the best break index within a text window.
215
+ * Prefers newlines over whitespace, avoids breaking inside parentheses.
216
+ */
217
+ function findBreakIndex(window) {
218
+ let lastNewline = -1;
219
+ let lastWhitespace = -1;
220
+ let parenDepth = 0;
221
+ for (let i = 0; i < window.length; i++) {
222
+ const char = window[i];
223
+ if (char === "(") {
224
+ parenDepth++;
225
+ continue;
226
+ }
227
+ if (char === ")" && parenDepth > 0) {
228
+ parenDepth--;
229
+ continue;
230
+ }
231
+ if (parenDepth === 0) {
232
+ if (char === "\n") lastNewline = i;
233
+ else if (/\s/.test(char)) lastWhitespace = i;
234
+ }
235
+ }
236
+ return lastNewline > 0 ? lastNewline : lastWhitespace;
237
+ }
238
+ function markdownToSignalTextChunks(markdown, limit, options = {}) {
239
+ const chunks = chunkMarkdownIR(markdownToIR(markdown ?? "", {
240
+ linkify: true,
241
+ enableSpoilers: true,
242
+ headingStyle: "bold",
243
+ blockquotePrefix: "> ",
244
+ tableMode: options.tableMode
245
+ }), limit);
246
+ const results = [];
247
+ for (const chunk of chunks) {
248
+ const rendered = renderSignalText(chunk);
249
+ if (rendered.text.length > limit) results.push(...splitSignalFormattedText(rendered, limit));
250
+ else results.push(rendered);
251
+ }
252
+ return results;
253
+ }
254
+ //#endregion
255
+ //#region src/signal/client.ts
256
+ const DEFAULT_TIMEOUT_MS = 1e4;
257
+ function normalizeBaseUrl(url) {
258
+ const trimmed = url.trim();
259
+ if (!trimmed) throw new Error("Signal base URL is required");
260
+ if (/^https?:\/\//i.test(trimmed)) return trimmed.replace(/\/+$/, "");
261
+ return `http://${trimmed}`.replace(/\/+$/, "");
262
+ }
263
+ function getRequiredFetch() {
264
+ const fetchImpl = resolveFetch();
265
+ if (!fetchImpl) throw new Error("fetch is not available");
266
+ return fetchImpl;
267
+ }
268
+ function parseSignalRpcResponse(text, status) {
269
+ let parsed;
270
+ try {
271
+ parsed = JSON.parse(text);
272
+ } catch (err) {
273
+ throw new Error(`Signal RPC returned malformed JSON (status ${status})`, { cause: err });
274
+ }
275
+ if (!parsed || typeof parsed !== "object") throw new Error(`Signal RPC returned invalid response envelope (status ${status})`);
276
+ const rpc = parsed;
277
+ const hasResult = Object.hasOwn(rpc, "result");
278
+ if (!rpc.error && !hasResult) throw new Error(`Signal RPC returned invalid response envelope (status ${status})`);
279
+ return rpc;
280
+ }
281
+ async function signalRpcRequest(method, params, opts) {
282
+ const baseUrl = normalizeBaseUrl(opts.baseUrl);
283
+ const id = generateSecureUuid();
284
+ const body = JSON.stringify({
285
+ jsonrpc: "2.0",
286
+ method,
287
+ params,
288
+ id
289
+ });
290
+ const res = await fetchWithTimeout(`${baseUrl}/api/v1/rpc`, {
291
+ method: "POST",
292
+ headers: { "Content-Type": "application/json" },
293
+ body
294
+ }, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS, getRequiredFetch());
295
+ if (res.status === 201) return;
296
+ const text = await res.text();
297
+ if (!text) throw new Error(`Signal RPC empty response (status ${res.status})`);
298
+ const parsed = parseSignalRpcResponse(text, res.status);
299
+ if (parsed.error) {
300
+ const code = parsed.error.code ?? "unknown";
301
+ const msg = parsed.error.message ?? "Signal RPC error";
302
+ throw new Error(`Signal RPC ${code}: ${msg}`);
303
+ }
304
+ return parsed.result;
305
+ }
306
+ async function signalCheck(baseUrl, timeoutMs = DEFAULT_TIMEOUT_MS) {
307
+ const normalized = normalizeBaseUrl(baseUrl);
308
+ try {
309
+ const res = await fetchWithTimeout(`${normalized}/api/v1/check`, { method: "GET" }, timeoutMs, getRequiredFetch());
310
+ if (!res.ok) return {
311
+ ok: false,
312
+ status: res.status,
313
+ error: `HTTP ${res.status}`
314
+ };
315
+ return {
316
+ ok: true,
317
+ status: res.status,
318
+ error: null
319
+ };
320
+ } catch (err) {
321
+ return {
322
+ ok: false,
323
+ status: null,
324
+ error: err instanceof Error ? err.message : String(err)
325
+ };
326
+ }
327
+ }
328
+ async function streamSignalEvents(params) {
329
+ const baseUrl = normalizeBaseUrl(params.baseUrl);
330
+ const url = new URL(`${baseUrl}/api/v1/events`);
331
+ if (params.account) url.searchParams.set("account", params.account);
332
+ const fetchImpl = resolveFetch();
333
+ if (!fetchImpl) throw new Error("fetch is not available");
334
+ const res = await fetchImpl(url, {
335
+ method: "GET",
336
+ headers: { Accept: "text/event-stream" },
337
+ signal: params.abortSignal
338
+ });
339
+ if (!res.ok || !res.body) throw new Error(`Signal SSE failed (${res.status} ${res.statusText || "error"})`);
340
+ const reader = res.body.getReader();
341
+ const decoder = new TextDecoder();
342
+ let buffer = "";
343
+ let currentEvent = {};
344
+ const flushEvent = () => {
345
+ if (!currentEvent.data && !currentEvent.event && !currentEvent.id) return;
346
+ params.onEvent({
347
+ event: currentEvent.event,
348
+ data: currentEvent.data,
349
+ id: currentEvent.id
350
+ });
351
+ currentEvent = {};
352
+ };
353
+ while (true) {
354
+ const { value, done } = await reader.read();
355
+ if (done) break;
356
+ buffer += decoder.decode(value, { stream: true });
357
+ let lineEnd = buffer.indexOf("\n");
358
+ while (lineEnd !== -1) {
359
+ let line = buffer.slice(0, lineEnd);
360
+ buffer = buffer.slice(lineEnd + 1);
361
+ if (line.endsWith("\r")) line = line.slice(0, -1);
362
+ if (line === "") {
363
+ flushEvent();
364
+ lineEnd = buffer.indexOf("\n");
365
+ continue;
366
+ }
367
+ if (line.startsWith(":")) {
368
+ lineEnd = buffer.indexOf("\n");
369
+ continue;
370
+ }
371
+ const [rawField, ...rest] = line.split(":");
372
+ const field = rawField.trim();
373
+ const rawValue = rest.join(":");
374
+ const value = rawValue.startsWith(" ") ? rawValue.slice(1) : rawValue;
375
+ if (field === "event") currentEvent.event = value;
376
+ else if (field === "data") currentEvent.data = currentEvent.data ? `${currentEvent.data}\n${value}` : value;
377
+ else if (field === "id") currentEvent.id = value;
378
+ lineEnd = buffer.indexOf("\n");
379
+ }
380
+ }
381
+ flushEvent();
382
+ }
383
+ //#endregion
384
+ //#region src/signal/rpc-context.ts
385
+ function resolveSignalRpcContext(opts, accountInfo) {
386
+ const hasBaseUrl = Boolean(opts.baseUrl?.trim());
387
+ const hasAccount = Boolean(opts.account?.trim());
388
+ const resolvedAccount = accountInfo || (!hasBaseUrl || !hasAccount ? resolveSignalAccount({
389
+ cfg: loadConfig(),
390
+ accountId: opts.accountId
391
+ }) : void 0);
392
+ const baseUrl = opts.baseUrl?.trim() || resolvedAccount?.baseUrl;
393
+ if (!baseUrl) throw new Error("Signal base URL is required");
394
+ return {
395
+ baseUrl,
396
+ account: opts.account?.trim() || resolvedAccount?.config.account?.trim()
397
+ };
398
+ }
399
+ //#endregion
400
+ //#region src/signal/send.ts
401
+ function parseTarget(raw) {
402
+ let value = raw.trim();
403
+ if (!value) throw new Error("Signal recipient is required");
404
+ if (value.toLowerCase().startsWith("signal:")) value = value.slice(7).trim();
405
+ const normalized = value.toLowerCase();
406
+ if (normalized.startsWith("group:")) return {
407
+ type: "group",
408
+ groupId: value.slice(6).trim()
409
+ };
410
+ if (normalized.startsWith("username:")) return {
411
+ type: "username",
412
+ username: value.slice(9).trim()
413
+ };
414
+ if (normalized.startsWith("u:")) return {
415
+ type: "username",
416
+ username: value.trim()
417
+ };
418
+ return {
419
+ type: "recipient",
420
+ recipient: value
421
+ };
422
+ }
423
+ function buildTargetParams(target, allow) {
424
+ if (target.type === "recipient") {
425
+ if (!allow.recipient) return null;
426
+ return { recipient: [target.recipient] };
427
+ }
428
+ if (target.type === "group") {
429
+ if (!allow.group) return null;
430
+ return { groupId: target.groupId };
431
+ }
432
+ if (target.type === "username") {
433
+ if (!allow.username) return null;
434
+ return { username: [target.username] };
435
+ }
436
+ return null;
437
+ }
438
+ async function sendMessageSignal(to, text, opts = {}) {
439
+ const cfg = opts.cfg ?? loadConfig();
440
+ const accountInfo = resolveSignalAccount({
441
+ cfg,
442
+ accountId: opts.accountId
443
+ });
444
+ const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
445
+ const target = parseTarget(to);
446
+ let message = text ?? "";
447
+ let messageFromPlaceholder = false;
448
+ let textStyles = [];
449
+ const textMode = opts.textMode ?? "markdown";
450
+ const maxBytes = (() => {
451
+ if (typeof opts.maxBytes === "number") return opts.maxBytes;
452
+ if (typeof accountInfo.config.mediaMaxMb === "number") return accountInfo.config.mediaMaxMb * 1024 * 1024;
453
+ if (typeof cfg.agents?.defaults?.mediaMaxMb === "number") return cfg.agents.defaults.mediaMaxMb * 1024 * 1024;
454
+ return 8 * 1024 * 1024;
455
+ })();
456
+ let attachments;
457
+ if (opts.mediaUrl?.trim()) {
458
+ const resolved = await resolveOutboundAttachmentFromUrl(opts.mediaUrl.trim(), maxBytes, { localRoots: opts.mediaLocalRoots });
459
+ attachments = [resolved.path];
460
+ const kind = kindFromMime(resolved.contentType ?? void 0);
461
+ if (!message && kind) {
462
+ message = kind === "image" ? "<media:image>" : `<media:${kind}>`;
463
+ messageFromPlaceholder = true;
464
+ }
465
+ }
466
+ if (message.trim() && !messageFromPlaceholder) if (textMode === "plain") textStyles = opts.textStyles ?? [];
467
+ else {
468
+ const tableMode = resolveMarkdownTableMode({
469
+ cfg,
470
+ channel: "signal",
471
+ accountId: accountInfo.accountId
472
+ });
473
+ const formatted = markdownToSignalText(message, { tableMode });
474
+ message = formatted.text;
475
+ textStyles = formatted.styles;
476
+ }
477
+ if (!message.trim() && (!attachments || attachments.length === 0)) throw new Error("Signal send requires text or media");
478
+ const params = { message };
479
+ if (textStyles.length > 0) params["text-style"] = textStyles.map((style) => `${style.start}:${style.length}:${style.style}`);
480
+ if (account) params.account = account;
481
+ if (attachments && attachments.length > 0) params.attachments = attachments;
482
+ const targetParams = buildTargetParams(target, {
483
+ recipient: true,
484
+ group: true,
485
+ username: true
486
+ });
487
+ if (!targetParams) throw new Error("Signal recipient is required");
488
+ Object.assign(params, targetParams);
489
+ const timestamp = (await signalRpcRequest("send", params, {
490
+ baseUrl,
491
+ timeoutMs: opts.timeoutMs
492
+ }))?.timestamp;
493
+ return {
494
+ messageId: timestamp ? String(timestamp) : "unknown",
495
+ timestamp
496
+ };
497
+ }
498
+ async function sendTypingSignal(to, opts = {}) {
499
+ const { baseUrl, account } = resolveSignalRpcContext(opts);
500
+ const targetParams = buildTargetParams(parseTarget(to), {
501
+ recipient: true,
502
+ group: true
503
+ });
504
+ if (!targetParams) return false;
505
+ const params = { ...targetParams };
506
+ if (account) params.account = account;
507
+ if (opts.stop) params.stop = true;
508
+ await signalRpcRequest("sendTyping", params, {
509
+ baseUrl,
510
+ timeoutMs: opts.timeoutMs
511
+ });
512
+ return true;
513
+ }
514
+ async function sendReadReceiptSignal(to, targetTimestamp, opts = {}) {
515
+ if (!Number.isFinite(targetTimestamp) || targetTimestamp <= 0) return false;
516
+ const { baseUrl, account } = resolveSignalRpcContext(opts);
517
+ const targetParams = buildTargetParams(parseTarget(to), { recipient: true });
518
+ if (!targetParams) return false;
519
+ const params = {
520
+ ...targetParams,
521
+ targetTimestamp,
522
+ type: opts.type ?? "read"
523
+ };
524
+ if (account) params.account = account;
525
+ await signalRpcRequest("sendReceipt", params, {
526
+ baseUrl,
527
+ timeoutMs: opts.timeoutMs
528
+ });
529
+ return true;
530
+ }
531
+ //#endregion
532
+ export { signalCheck as a, markdownToSignalTextChunks as c, resolveSignalRpcContext as i, sendReadReceiptSignal as n, signalRpcRequest as o, sendTypingSignal as r, streamSignalEvents as s, sendMessageSignal as t };