chore: migrate to oxlint and oxfmt

Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
Peter Steinberger
2026-01-14 14:31:43 +00:00
parent 912ebffc63
commit c379191f80
1480 changed files with 28608 additions and 43547 deletions

View File

@@ -1,9 +1,6 @@
import type { ClawdbotConfig } from "../config/config.js";
import type { SignalAccountConfig } from "../config/types.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
} from "../routing/session-key.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
export type ResolvedSignalAccount = {
accountId: string;
@@ -41,12 +38,10 @@ function resolveAccountConfig(
return accounts[accountId] as SignalAccountConfig | undefined;
}
function mergeSignalAccountConfig(
cfg: ClawdbotConfig,
accountId: string,
): SignalAccountConfig {
const { accounts: _ignored, ...base } = (cfg.channels?.signal ??
{}) as SignalAccountConfig & { accounts?: unknown };
function mergeSignalAccountConfig(cfg: ClawdbotConfig, accountId: string): SignalAccountConfig {
const { accounts: _ignored, ...base } = (cfg.channels?.signal ?? {}) as SignalAccountConfig & {
accounts?: unknown;
};
const account = resolveAccountConfig(cfg, accountId) ?? {};
return { ...base, ...account };
}
@@ -65,11 +60,11 @@ export function resolveSignalAccount(params: {
const baseUrl = merged.httpUrl?.trim() || `http://${host}:${port}`;
const configured = Boolean(
merged.account?.trim() ||
merged.httpUrl?.trim() ||
merged.cliPath?.trim() ||
merged.httpHost?.trim() ||
typeof merged.httpPort === "number" ||
typeof merged.autoStart === "boolean",
merged.httpUrl?.trim() ||
merged.cliPath?.trim() ||
merged.httpHost?.trim() ||
typeof merged.httpPort === "number" ||
typeof merged.autoStart === "boolean",
);
return {
accountId,
@@ -81,9 +76,7 @@ export function resolveSignalAccount(params: {
};
}
export function listEnabledSignalAccounts(
cfg: ClawdbotConfig,
): ResolvedSignalAccount[] {
export function listEnabledSignalAccounts(cfg: ClawdbotConfig): ResolvedSignalAccount[] {
return listSignalAccountIds(cfg)
.map((accountId) => resolveSignalAccount({ cfg, accountId }))
.filter((account) => account.enabled);

View File

@@ -35,11 +35,7 @@ function normalizeBaseUrl(url: string): string {
return `http://${trimmed}`.replace(/\/+$/, "");
}
async function fetchWithTimeout(
url: string,
init: RequestInit,
timeoutMs: number,
) {
async function fetchWithTimeout(url: string, init: RequestInit, timeoutMs: number) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
@@ -93,11 +89,7 @@ export async function signalCheck(
): Promise<{ ok: boolean; status?: number | null; error?: string | null }> {
const normalized = normalizeBaseUrl(baseUrl);
try {
const res = await fetchWithTimeout(
`${normalized}/api/v1/check`,
{ method: "GET" },
timeoutMs,
);
const res = await fetchWithTimeout(`${normalized}/api/v1/check`, { method: "GET" }, timeoutMs);
if (!res.ok) {
return { ok: false, status: res.status, error: `HTTP ${res.status}` };
}
@@ -127,9 +119,7 @@ export async function streamSignalEvents(params: {
signal: params.abortSignal,
});
if (!res.ok || !res.body) {
throw new Error(
`Signal SSE failed (${res.status} ${res.statusText || "error"})`,
);
throw new Error(`Signal SSE failed (${res.status} ${res.statusText || "error"})`);
}
const reader = res.body.getReader();
@@ -173,9 +163,7 @@ export async function streamSignalEvents(params: {
if (field === "event") {
currentEvent.event = value;
} else if (field === "data") {
currentEvent.data = currentEvent.data
? `${currentEvent.data}\n${value}`
: value;
currentEvent.data = currentEvent.data ? `${currentEvent.data}\n${value}` : value;
} else if (field === "id") {
currentEvent.id = value;
}

View File

@@ -4,9 +4,7 @@ import { classifySignalCliLogLine } from "./daemon.js";
describe("classifySignalCliLogLine", () => {
it("treats INFO/DEBUG as log (even if emitted on stderr)", () => {
expect(classifySignalCliLogLine("INFO DaemonCommand - Started")).toBe(
"log",
);
expect(classifySignalCliLogLine("INFO DaemonCommand - Started")).toBe("log");
expect(classifySignalCliLogLine("DEBUG Something")).toBe("log");
});
@@ -17,12 +15,8 @@ describe("classifySignalCliLogLine", () => {
});
it("treats failures without explicit severity as error", () => {
expect(
classifySignalCliLogLine("Failed to initialize HTTP Server - oops"),
).toBe("error");
expect(classifySignalCliLogLine('Exception in thread "main"')).toBe(
"error",
);
expect(classifySignalCliLogLine("Failed to initialize HTTP Server - oops")).toBe("error");
expect(classifySignalCliLogLine('Exception in thread "main"')).toBe("error");
});
it("returns null for empty lines", () => {

View File

@@ -9,8 +9,7 @@ type SignalAllowEntry =
| { kind: "phone"; e164: string }
| { kind: "uuid"; raw: string };
const UUID_HYPHENATED_RE =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const UUID_HYPHENATED_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const UUID_COMPACT_RE = /^[0-9a-f]{32}$/i;
function looksLikeUuid(value: string): boolean {
@@ -88,10 +87,7 @@ function parseSignalAllowEntry(entry: string): SignalAllowEntry | null {
return { kind: "phone", e164: normalizeE164(stripped) };
}
export function isSignalSenderAllowed(
sender: SignalSender,
allowFrom: string[],
): boolean {
export function isSignalSenderAllowed(sender: SignalSender, allowFrom: string[]): boolean {
if (allowFrom.length === 0) return false;
const parsed = allowFrom
.map(parseSignalAllowEntry)

View File

@@ -28,10 +28,8 @@ vi.mock("./send.js", () => ({
}));
vi.mock("../pairing/pairing-store.js", () => ({
readChannelAllowFromStore: (...args: unknown[]) =>
readAllowFromStoreMock(...args),
upsertChannelPairingRequest: (...args: unknown[]) =>
upsertPairingRequestMock(...args),
readChannelAllowFromStore: (...args: unknown[]) => readAllowFromStoreMock(...args),
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
}));
vi.mock("../config/sessions.js", () => ({
@@ -70,9 +68,7 @@ beforeEach(() => {
signalCheckMock.mockReset().mockResolvedValue({});
signalRpcRequestMock.mockReset().mockResolvedValue({});
readAllowFromStoreMock.mockReset().mockResolvedValue([]);
upsertPairingRequestMock
.mockReset()
.mockResolvedValue({ code: "PAIRCODE", created: true });
upsertPairingRequestMock.mockReset().mockResolvedValue({ code: "PAIRCODE", created: true });
resetSystemEventsForTest();
});

View File

@@ -2,10 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js";
import type { ClawdbotConfig } from "../config/config.js";
import {
peekSystemEvents,
resetSystemEventsForTest,
} from "../infra/system-events.js";
import { peekSystemEvents, resetSystemEventsForTest } from "../infra/system-events.js";
import { resolveAgentRoute } from "../routing/resolve-route.js";
import { normalizeE164 } from "../utils.js";
import { monitorSignalProvider } from "./monitor.js";
@@ -34,10 +31,8 @@ vi.mock("./send.js", () => ({
}));
vi.mock("../pairing/pairing-store.js", () => ({
readChannelAllowFromStore: (...args: unknown[]) =>
readAllowFromStoreMock(...args),
upsertChannelPairingRequest: (...args: unknown[]) =>
upsertPairingRequestMock(...args),
readChannelAllowFromStore: (...args: unknown[]) => readAllowFromStoreMock(...args),
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
}));
vi.mock("../config/sessions.js", () => ({
@@ -76,9 +71,7 @@ beforeEach(() => {
signalCheckMock.mockReset().mockResolvedValue({});
signalRpcRequestMock.mockReset().mockResolvedValue({});
readAllowFromStoreMock.mockReset().mockResolvedValue([]);
upsertPairingRequestMock
.mockReset()
.mockResolvedValue({ code: "PAIRCODE", created: true });
upsertPairingRequestMock.mockReset().mockResolvedValue({ code: "PAIRCODE", created: true });
resetSystemEventsForTest();
});
@@ -165,12 +158,8 @@ describe("monitorSignalProvider tool results", () => {
expect(replyMock).not.toHaveBeenCalled();
expect(upsertPairingRequestMock).toHaveBeenCalled();
expect(sendMock).toHaveBeenCalledTimes(1);
expect(String(sendMock.mock.calls[0]?.[1] ?? "")).toContain(
"Your Signal number: +15550001111",
);
expect(String(sendMock.mock.calls[0]?.[1] ?? "")).toContain(
"Pairing code: PAIRCODE",
);
expect(String(sendMock.mock.calls[0]?.[1] ?? "")).toContain("Your Signal number: +15550001111");
expect(String(sendMock.mock.calls[0]?.[1] ?? "")).toContain("Pairing code: PAIRCODE");
});
it("ignores reaction-only messages", async () => {
@@ -299,9 +288,7 @@ describe("monitorSignalProvider tool results", () => {
peer: { kind: "dm", id: normalizeE164("+15550001111") },
});
const events = peekSystemEvents(route.sessionKey);
expect(events.some((text) => text.includes("Signal reaction added"))).toBe(
true,
);
expect(events.some((text) => text.includes("Signal reaction added"))).toBe(true);
});
it("notifies on own reactions when target includes uuid + phone", async () => {
@@ -357,9 +344,7 @@ describe("monitorSignalProvider tool results", () => {
peer: { kind: "dm", id: normalizeE164("+15550001111") },
});
const events = peekSystemEvents(route.sessionKey);
expect(events.some((text) => text.includes("Signal reaction added"))).toBe(
true,
);
expect(events.some((text) => text.includes("Signal reaction added"))).toBe(true);
});
it("processes messages when reaction metadata is present", async () => {

View File

@@ -1,8 +1,5 @@
import { chunkText, resolveTextChunkLimit } from "../auto-reply/chunk.js";
import {
DEFAULT_GROUP_HISTORY_LIMIT,
type HistoryEntry,
} from "../auto-reply/reply/history.js";
import { DEFAULT_GROUP_HISTORY_LIMIT, type HistoryEntry } from "../auto-reply/reply/history.js";
import type { ReplyPayload } from "../auto-reply/types.js";
import type { ClawdbotConfig } from "../config/config.js";
import { loadConfig } from "../config/config.js";
@@ -80,9 +77,7 @@ type SignalReactionTarget = {
display: string;
};
function resolveSignalReactionTargets(
reaction: SignalReactionMessage,
): SignalReactionTarget[] {
function resolveSignalReactionTargets(reaction: SignalReactionMessage): SignalReactionTarget[] {
const targets: SignalReactionTarget[] = [];
const uuid = reaction.targetAuthorUuid?.trim();
if (uuid) {
@@ -102,12 +97,8 @@ function isSignalReactionMessage(
if (!reaction) return false;
const emoji = reaction.emoji?.trim();
const timestamp = reaction.targetSentTimestamp;
const hasTarget = Boolean(
reaction.targetAuthor?.trim() || reaction.targetAuthorUuid?.trim(),
);
return Boolean(
emoji && typeof timestamp === "number" && timestamp > 0 && hasTarget,
);
const hasTarget = Boolean(reaction.targetAuthor?.trim() || reaction.targetAuthorUuid?.trim());
return Boolean(emoji && typeof timestamp === "number" && timestamp > 0 && hasTarget);
}
function shouldEmitSignalReactionNotification(params: {
@@ -146,12 +137,8 @@ function buildSignalReactionSystemEventText(params: {
groupLabel?: string;
}) {
const base = `Signal reaction added: ${params.emojiLabel} by ${params.actorLabel} msg ${params.messageId}`;
const withTarget = params.targetLabel
? `${base} from ${params.targetLabel}`
: base;
return params.groupLabel
? `${withTarget} in ${params.groupLabel}`
: withTarget;
const withTarget = params.targetLabel ? `${base} from ${params.targetLabel}` : base;
return params.groupLabel ? `${withTarget} in ${params.groupLabel}` : withTarget;
}
async function waitForSignalDaemonReady(params: {
@@ -167,15 +154,12 @@ async function waitForSignalDaemonReady(params: {
if (params.abortSignal?.aborted) return;
const res = await signalCheck(params.baseUrl, 1000);
if (res.ok) return;
lastError =
res.error ?? (res.status ? `HTTP ${res.status}` : "unreachable");
lastError = res.error ?? (res.status ? `HTTP ${res.status}` : "unreachable");
await new Promise((r) => setTimeout(r, 150));
}
params.runtime.error?.(
danger(
`daemon not ready after ${params.timeoutMs}ms (${lastError ?? "unknown error"})`,
),
danger(`daemon not ready after ${params.timeoutMs}ms (${lastError ?? "unknown error"})`),
);
throw new Error(`signal daemon not ready (${lastError ?? "unknown error"})`);
}
@@ -203,11 +187,9 @@ async function fetchAttachment(params: {
else if (params.sender) rpcParams.recipient = params.sender;
else return null;
const result = await signalRpcRequest<{ data?: string }>(
"getAttachment",
rpcParams,
{ baseUrl: params.baseUrl },
);
const result = await signalRpcRequest<{ data?: string }>("getAttachment", rpcParams, {
baseUrl: params.baseUrl,
});
if (!result?.data) return null;
const buffer = Buffer.from(result.data, "base64");
const saved = await saveMediaBuffer(
@@ -229,19 +211,9 @@ async function deliverReplies(params: {
maxBytes: number;
textLimit: number;
}) {
const {
replies,
target,
baseUrl,
account,
accountId,
runtime,
maxBytes,
textLimit,
} = params;
const { replies, target, baseUrl, account, accountId, runtime, maxBytes, textLimit } = params;
for (const payload of replies) {
const mediaList =
payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
const mediaList = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
const text = payload.text ?? "";
if (!text && mediaList.length === 0) continue;
if (mediaList.length === 0) {
@@ -271,9 +243,7 @@ async function deliverReplies(params: {
}
}
export async function monitorSignalProvider(
opts: MonitorSignalOpts = {},
): Promise<void> {
export async function monitorSignalProvider(opts: MonitorSignalOpts = {}): Promise<void> {
const runtime = resolveRuntime(opts);
const cfg = opts.config ?? loadConfig();
const accountInfo = resolveSignalAccount({
@@ -291,9 +261,7 @@ export async function monitorSignalProvider(
const baseUrl = opts.baseUrl?.trim() || accountInfo.baseUrl;
const account = opts.account?.trim() || accountInfo.config.account?.trim();
const dmPolicy = accountInfo.config.dmPolicy ?? "pairing";
const allowFrom = normalizeAllowList(
opts.allowFrom ?? accountInfo.config.allowFrom,
);
const allowFrom = normalizeAllowList(opts.allowFrom ?? accountInfo.config.allowFrom);
const groupAllowFrom = normalizeAllowList(
opts.groupAllowFrom ??
accountInfo.config.groupAllowFrom ??
@@ -303,24 +271,16 @@ export async function monitorSignalProvider(
);
const groupPolicy = accountInfo.config.groupPolicy ?? "allowlist";
const reactionMode = accountInfo.config.reactionNotifications ?? "own";
const reactionAllowlist = normalizeAllowList(
accountInfo.config.reactionAllowlist,
);
const mediaMaxBytes =
(opts.mediaMaxMb ?? accountInfo.config.mediaMaxMb ?? 8) * 1024 * 1024;
const ignoreAttachments =
opts.ignoreAttachments ?? accountInfo.config.ignoreAttachments ?? false;
const reactionAllowlist = normalizeAllowList(accountInfo.config.reactionAllowlist);
const mediaMaxBytes = (opts.mediaMaxMb ?? accountInfo.config.mediaMaxMb ?? 8) * 1024 * 1024;
const ignoreAttachments = opts.ignoreAttachments ?? accountInfo.config.ignoreAttachments ?? false;
const autoStart =
opts.autoStart ??
accountInfo.config.autoStart ??
!accountInfo.config.httpUrl;
const autoStart = opts.autoStart ?? accountInfo.config.autoStart ?? !accountInfo.config.httpUrl;
let daemonHandle: ReturnType<typeof spawnSignalDaemon> | null = null;
if (autoStart) {
const cliPath = opts.cliPath ?? accountInfo.config.cliPath ?? "signal-cli";
const httpHost =
opts.httpHost ?? accountInfo.config.httpHost ?? "127.0.0.1";
const httpHost = opts.httpHost ?? accountInfo.config.httpHost ?? "127.0.0.1";
const httpPort = opts.httpPort ?? accountInfo.config.httpPort ?? 8080;
daemonHandle = spawnSignalDaemon({
cliPath,
@@ -328,11 +288,9 @@ export async function monitorSignalProvider(
httpHost,
httpPort,
receiveMode: opts.receiveMode ?? accountInfo.config.receiveMode,
ignoreAttachments:
opts.ignoreAttachments ?? accountInfo.config.ignoreAttachments,
ignoreAttachments: opts.ignoreAttachments ?? accountInfo.config.ignoreAttachments,
ignoreStories: opts.ignoreStories ?? accountInfo.config.ignoreStories,
sendReadReceipts:
opts.sendReadReceipts ?? accountInfo.config.sendReadReceipts,
sendReadReceipts: opts.sendReadReceipts ?? accountInfo.config.sendReadReceipts,
runtime,
});
}

View File

@@ -1,13 +1,7 @@
import {
resolveEffectiveMessagesConfig,
resolveHumanDelayConfig,
} from "../../agents/identity.js";
import { resolveEffectiveMessagesConfig, resolveHumanDelayConfig } from "../../agents/identity.js";
import { formatAgentEnvelope } from "../../auto-reply/envelope.js";
import { dispatchReplyFromConfig } from "../../auto-reply/reply/dispatch-from-config.js";
import {
buildHistoryContextFromMap,
clearHistoryEntries,
} from "../../auto-reply/reply/history.js";
import { buildHistoryContextFromMap, clearHistoryEntries } from "../../auto-reply/reply/history.js";
import { createReplyDispatcher } from "../../auto-reply/reply/reply-dispatcher.js";
import { resolveStorePath, updateLastRoute } from "../../config/sessions.js";
import { danger, logVerbose, shouldLogVerbose } from "../../globals.js";
@@ -31,10 +25,7 @@ import {
} from "../identity.js";
import { sendMessageSignal } from "../send.js";
import type {
SignalEventHandlerDeps,
SignalReceivePayload,
} from "./event-handler.types.js";
import type { SignalEventHandlerDeps, SignalReceivePayload } from "./event-handler.types.js";
export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
return async (event: { event?: string; data?: string }) => {
@@ -60,8 +51,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
if (sender.e164 === normalizeE164(deps.account)) return;
}
const dataMessage =
envelope.dataMessage ?? envelope.editMessage?.dataMessage;
const dataMessage = envelope.dataMessage ?? envelope.editMessage?.dataMessage;
const reaction = deps.isSignalReactionMessage(envelope.reactionMessage)
? envelope.reactionMessage
: deps.isSignalReactionMessage(dataMessage?.reaction)
@@ -70,8 +60,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
const messageText = (dataMessage?.message ?? "").trim();
const quoteText = dataMessage?.quote?.text?.trim() ?? "";
const hasBodyContent =
Boolean(messageText || quoteText) ||
Boolean(!reaction && dataMessage?.attachments?.length);
Boolean(messageText || quoteText) || Boolean(!reaction && dataMessage?.attachments?.length);
if (reaction && !hasBodyContent) {
if (reaction.isRemove) return; // Ignore reaction removals
@@ -102,9 +91,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
id: isGroup ? (groupId ?? "unknown") : senderPeerId,
},
});
const groupLabel = isGroup
? `${groupName ?? "Signal Group"} id:${groupId}`
: undefined;
const groupLabel = isGroup ? `${groupName ?? "Signal Group"} id:${groupId}` : undefined;
const messageId = reaction.targetSentTimestamp
? String(reaction.targetSentTimestamp)
: "unknown";
@@ -141,15 +128,11 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
const groupId = dataMessage.groupInfo?.groupId ?? undefined;
const groupName = dataMessage.groupInfo?.groupName ?? undefined;
const isGroup = Boolean(groupId);
const storeAllowFrom = await readChannelAllowFromStore("signal").catch(
() => [],
);
const storeAllowFrom = await readChannelAllowFromStore("signal").catch(() => []);
const effectiveDmAllow = [...deps.allowFrom, ...storeAllowFrom];
const effectiveGroupAllow = [...deps.groupAllowFrom, ...storeAllowFrom];
const dmAllowed =
deps.dmPolicy === "open"
? true
: isSignalSenderAllowed(sender, effectiveDmAllow);
deps.dmPolicy === "open" ? true : isSignalSenderAllowed(sender, effectiveDmAllow);
if (!isGroup) {
if (deps.dmPolicy === "disabled") return;
@@ -179,15 +162,11 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
},
);
} catch (err) {
logVerbose(
`signal pairing reply failed for ${senderId}: ${String(err)}`,
);
logVerbose(`signal pairing reply failed for ${senderId}: ${String(err)}`);
}
}
} else {
logVerbose(
`Blocked signal sender ${senderDisplay} (dmPolicy=${deps.dmPolicy})`,
);
logVerbose(`Blocked signal sender ${senderDisplay} (dmPolicy=${deps.dmPolicy})`);
}
return;
}
@@ -198,15 +177,11 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
}
if (isGroup && deps.groupPolicy === "allowlist") {
if (effectiveGroupAllow.length === 0) {
logVerbose(
"Blocked signal group message (groupPolicy: allowlist, no groupAllowFrom)",
);
logVerbose("Blocked signal group message (groupPolicy: allowlist, no groupAllowFrom)");
return;
}
if (!isSignalSenderAllowed(sender, effectiveGroupAllow)) {
logVerbose(
`Blocked signal group sender ${senderDisplay} (not in groupAllowFrom)`,
);
logVerbose(`Blocked signal group sender ${senderDisplay} (not in groupAllowFrom)`);
return;
}
}
@@ -233,8 +208,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
});
if (fetched) {
mediaPath = fetched.path;
mediaType =
fetched.contentType ?? firstAttachment.contentType ?? undefined;
mediaType = fetched.contentType ?? firstAttachment.contentType ?? undefined;
}
} catch (err) {
deps.runtime.error?.(danger(`attachment fetch failed: ${String(err)}`));
@@ -243,11 +217,9 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
const kind = mediaKindFromMime(mediaType ?? undefined);
if (kind) placeholder = `<media:${kind}>`;
else if (dataMessage.attachments?.length)
placeholder = "<media:attachment>";
else if (dataMessage.attachments?.length) placeholder = "<media:attachment>";
const bodyText =
messageText || placeholder || dataMessage.quote?.text?.trim() || "";
const bodyText = messageText || placeholder || dataMessage.quote?.text?.trim() || "";
if (!bodyText) return;
const fromLabel = isGroup
@@ -271,9 +243,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
body: bodyText,
timestamp: envelope.timestamp ?? undefined,
messageId:
typeof envelope.timestamp === "number"
? String(envelope.timestamp)
: undefined,
typeof envelope.timestamp === "number" ? String(envelope.timestamp) : undefined,
},
currentMessage: combinedBody,
formatEntry: (entry) =>
@@ -300,9 +270,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
Body: combinedBody,
RawBody: bodyText,
CommandBody: bodyText,
From: isGroup
? `group:${groupId ?? "unknown"}`
: `signal:${senderRecipient}`,
From: isGroup ? `group:${groupId ?? "unknown"}` : `signal:${senderRecipient}`,
To: signalTo,
SessionKey: route.sessionKey,
AccountId: route.accountId,
@@ -338,15 +306,12 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
if (shouldLogVerbose()) {
const preview = body.slice(0, 200).replace(/\n/g, "\\n");
logVerbose(
`signal inbound: from=${ctxPayload.From} len=${body.length} preview="${preview}"`,
);
logVerbose(`signal inbound: from=${ctxPayload.From} len=${body.length} preview="${preview}"`);
}
let didSendReply = false;
const dispatcher = createReplyDispatcher({
responsePrefix: resolveEffectiveMessagesConfig(deps.cfg, route.agentId)
.responsePrefix,
responsePrefix: resolveEffectiveMessagesConfig(deps.cfg, route.agentId).responsePrefix,
humanDelay: resolveHumanDelayConfig(deps.cfg, route.agentId),
deliver: async (payload) => {
await deps.deliverReplies({
@@ -362,9 +327,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
didSendReply = true;
},
onError: (err, info) => {
deps.runtime.error?.(
danger(`signal ${info.kind} reply failed: ${String(err)}`),
);
deps.runtime.error?.(danger(`signal ${info.kind} reply failed: ${String(err)}`));
},
});
@@ -374,9 +337,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
dispatcher,
replyOptions: {
disableBlockStreaming:
typeof deps.blockStreaming === "boolean"
? !deps.blockStreaming
: undefined,
typeof deps.blockStreaming === "boolean" ? !deps.blockStreaming : undefined,
},
});
if (!queuedFinal) {

View File

@@ -1,11 +1,7 @@
import type { HistoryEntry } from "../../auto-reply/reply/history.js";
import type { ReplyPayload } from "../../auto-reply/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type {
DmPolicy,
GroupPolicy,
SignalReactionNotificationMode,
} from "../../config/types.js";
import type { DmPolicy, GroupPolicy, SignalReactionNotificationMode } from "../../config/types.js";
import type { RuntimeEnv } from "../../runtime.js";
import type { SignalSender } from "../identity.js";
@@ -98,9 +94,7 @@ export type SignalEventHandlerDeps = {
maxBytes: number;
textLimit: number;
}) => Promise<void>;
resolveSignalReactionTargets: (
reaction: SignalReactionMessage,
) => SignalReactionTarget[];
resolveSignalReactionTargets: (reaction: SignalReactionMessage) => SignalReactionTarget[];
isSignalReactionMessage: (
reaction: SignalReactionMessage | null | undefined,
) => reaction is SignalReactionMessage;

View File

@@ -17,10 +17,7 @@ function parseSignalVersion(value: unknown): string | null {
return null;
}
export async function probeSignal(
baseUrl: string,
timeoutMs: number,
): Promise<SignalProbe> {
export async function probeSignal(baseUrl: string, timeoutMs: number): Promise<SignalProbe> {
const started = Date.now();
const result: SignalProbe = {
ok: false,

View File

@@ -115,11 +115,10 @@ export async function sendMessageSignal(
params.username = [target.username];
}
const result = await signalRpcRequest<{ timestamp?: number }>(
"send",
params,
{ baseUrl, timeoutMs: opts.timeoutMs },
);
const result = await signalRpcRequest<{ timestamp?: number }>("send", params, {
baseUrl,
timeoutMs: opts.timeoutMs,
});
const timestamp = result?.timestamp;
return {
messageId: timestamp ? String(timestamp) : "unknown",

View File

@@ -53,18 +53,14 @@ export async function runSignalSseLoop({
if (abortSignal?.aborted) return;
reconnectAttempts += 1;
const delayMs = computeBackoff(reconnectPolicy, reconnectAttempts);
logReconnectVerbose(
`Signal SSE stream ended, reconnecting in ${delayMs / 1000}s...`,
);
logReconnectVerbose(`Signal SSE stream ended, reconnecting in ${delayMs / 1000}s...`);
await sleepWithAbort(delayMs, abortSignal);
} catch (err) {
if (abortSignal?.aborted) return;
runtime.error?.(`Signal SSE stream error: ${String(err)}`);
reconnectAttempts += 1;
const delayMs = computeBackoff(reconnectPolicy, reconnectAttempts);
runtime.log?.(
`Signal SSE connection lost, reconnecting in ${delayMs / 1000}s...`,
);
runtime.log?.(`Signal SSE connection lost, reconnecting in ${delayMs / 1000}s...`);
try {
await sleepWithAbort(delayMs, abortSignal);
} catch (sleepErr) {