From a67f4db5e2d4b1c1ed9ba1353f91f5a53a161105 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 26 Nov 2025 00:30:30 +0100 Subject: [PATCH] chore: format + lint --- src/config/config.ts | 66 ++++++++++++------------- src/index.core.test.ts | 104 +++++++++++++++++++-------------------- src/provider-web.test.ts | 72 +++++++++++++-------------- 3 files changed, 121 insertions(+), 121 deletions(-) diff --git a/src/config/config.ts b/src/config/config.ts index 4d8419c09..0faee0139 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -10,16 +10,16 @@ export type ClaudeOutputFormat = "text" | "json" | "stream-json"; export type SessionScope = "per-sender" | "global"; export type SessionConfig = { - scope?: SessionScope; - resetTriggers?: string[]; - idleMinutes?: number; - store?: string; - sessionArgNew?: string[]; - sessionArgResume?: string[]; - sessionArgBeforeBody?: boolean; - sendSystemOnce?: boolean; - sessionIntro?: string; - typingIntervalSeconds?: number; + scope?: SessionScope; + resetTriggers?: string[]; + idleMinutes?: number; + store?: string; + sessionArgNew?: string[]; + sessionArgResume?: string[]; + sessionArgBeforeBody?: boolean; + sendSystemOnce?: boolean; + sessionIntro?: string; + typingIntervalSeconds?: number; }; export type LoggingConfig = { @@ -28,29 +28,29 @@ export type LoggingConfig = { }; export type WarelayConfig = { - logging?: LoggingConfig; - inbound?: { - allowFrom?: string[]; // E.164 numbers allowed to trigger auto-reply (without whatsapp:) - transcribeAudio?: { - // Optional CLI to turn inbound audio into text; templated args, must output transcript to stdout. - command: string[]; - timeoutSeconds?: number; - }; - reply?: { - mode: ReplyMode; - text?: string; // for mode=text, can contain {{Body}} - command?: string[]; // for mode=command, argv with templates - cwd?: string; // working directory for command execution - template?: string; // prepend template string when building command/prompt - timeoutSeconds?: number; // optional command timeout; defaults to 600s - bodyPrefix?: string; // optional string prepended to Body before templating - mediaUrl?: string; // optional media attachment (path or URL) - session?: SessionConfig; - claudeOutputFormat?: ClaudeOutputFormat; // when command starts with `claude`, force an output format - mediaMaxMb?: number; // optional cap for outbound media (default 5MB) - typingIntervalSeconds?: number; // how often to refresh typing indicator while command runs - }; - }; + logging?: LoggingConfig; + inbound?: { + allowFrom?: string[]; // E.164 numbers allowed to trigger auto-reply (without whatsapp:) + transcribeAudio?: { + // Optional CLI to turn inbound audio into text; templated args, must output transcript to stdout. + command: string[]; + timeoutSeconds?: number; + }; + reply?: { + mode: ReplyMode; + text?: string; // for mode=text, can contain {{Body}} + command?: string[]; // for mode=command, argv with templates + cwd?: string; // working directory for command execution + template?: string; // prepend template string when building command/prompt + timeoutSeconds?: number; // optional command timeout; defaults to 600s + bodyPrefix?: string; // optional string prepended to Body before templating + mediaUrl?: string; // optional media attachment (path or URL) + session?: SessionConfig; + claudeOutputFormat?: ClaudeOutputFormat; // when command starts with `claude`, force an output format + mediaMaxMb?: number; // optional cap for outbound media (default 5MB) + typingIntervalSeconds?: number; // how often to refresh typing indicator while command runs + }; + }; }; export const CONFIG_PATH = path.join(os.homedir(), ".warelay", "warelay.json"); diff --git a/src/index.core.test.ts b/src/index.core.test.ts index d0e6bd21c..2cf46f91a 100644 --- a/src/index.core.test.ts +++ b/src/index.core.test.ts @@ -1,6 +1,6 @@ import crypto from "node:crypto"; -import net from "node:net"; import fs from "node:fs"; +import net from "node:net"; import os from "node:os"; import path from "node:path"; import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message.js"; @@ -144,11 +144,11 @@ describe("config and templating", () => { killed: false, }); - const result = await index.getReplyFromConfig( - { - Body: "", - From: "+1", - To: "+2", + const result = await index.getReplyFromConfig( + { + Body: "", + From: "+1", + To: "+2", MediaPath: "/tmp/voice.ogg", MediaType: "audio/ogg", }, @@ -157,15 +157,15 @@ describe("config and templating", () => { commandRunner, ); - expect(runExec).toHaveBeenCalled(); - expect(commandRunner).toHaveBeenCalled(); - const argv = commandRunner.mock.calls[0][0]; - const prompt = argv[argv.length - 1] as string; - expect(prompt).toContain("/tmp/voice.ogg"); - expect(prompt).toContain("Transcript:"); - expect(prompt).toContain("voice transcript"); - expect(result?.text).toBe("ok"); -}); + expect(runExec).toHaveBeenCalled(); + expect(commandRunner).toHaveBeenCalled(); + const argv = commandRunner.mock.calls[0][0]; + const prompt = argv[argv.length - 1] as string; + expect(prompt).toContain("/tmp/voice.ogg"); + expect(prompt).toContain("Transcript:"); + expect(prompt).toContain("voice transcript"); + expect(result?.text).toBe("ok"); + }); it("getReplyFromConfig skips transcription when not configured", async () => { const cfg = { @@ -640,44 +640,44 @@ describe("config and templating", () => { expect(onReplyStart.mock.calls.length).toBeGreaterThanOrEqual(3); }); - it("uses session typing interval override", async () => { - const onReplyStart = vi.fn(); - const runSpy = vi.spyOn(index, "runCommandWithTimeout").mockImplementation( - () => - new Promise((resolve) => - setTimeout( - () => - resolve({ - stdout: "done\n", - stderr: "", - code: 0, - signal: null, - killed: false, - }), - 120, - ), - ), - ); - const cfg = { - inbound: { - reply: { - mode: "command" as const, - command: ["echo", "{{Body}}"], - session: { typingIntervalSeconds: 0.02 }, - }, - }, - }; + it("uses session typing interval override", async () => { + const onReplyStart = vi.fn(); + const runSpy = vi.spyOn(index, "runCommandWithTimeout").mockImplementation( + () => + new Promise((resolve) => + setTimeout( + () => + resolve({ + stdout: "done\n", + stderr: "", + code: 0, + signal: null, + killed: false, + }), + 120, + ), + ), + ); + const cfg = { + inbound: { + reply: { + mode: "command" as const, + command: ["echo", "{{Body}}"], + session: { typingIntervalSeconds: 0.02 }, + }, + }, + }; - const promise = index.getReplyFromConfig( - { Body: "hi", From: "+1", To: "+2" }, - { onReplyStart }, - cfg, - runSpy, - ); - await new Promise((r) => setTimeout(r, 200)); - await promise; - expect(onReplyStart.mock.calls.length).toBeGreaterThanOrEqual(3); - }); + const promise = index.getReplyFromConfig( + { Body: "hi", From: "+1", To: "+2" }, + { onReplyStart }, + cfg, + runSpy, + ); + await new Promise((r) => setTimeout(r, 200)); + await promise; + expect(onReplyStart.mock.calls.length).toBeGreaterThanOrEqual(3); + }); it("injects Claude output format + print flag when configured", async () => { const runSpy = vi.spyOn(index, "runCommandWithTimeout").mockResolvedValue({ diff --git a/src/provider-web.test.ts b/src/provider-web.test.ts index 4ef875524..c86ffccb8 100644 --- a/src/provider-web.test.ts +++ b/src/provider-web.test.ts @@ -501,42 +501,42 @@ describe("provider-web", () => { fetchMock.mockRestore(); }); - it( - "compresses common formats to jpeg under the cap", - { timeout: 15_000 }, - async () => { - const formats = [ - { - name: "png", - mime: "image/png", - make: (buf: Buffer, opts: { width: number; height: number }) => - sharp(buf, { - raw: { width: opts.width, height: opts.height, channels: 3 }, - }) - .png({ compressionLevel: 0 }) - .toBuffer(), - }, - { - name: "jpeg", - mime: "image/jpeg", - make: (buf: Buffer, opts: { width: number; height: number }) => - sharp(buf, { - raw: { width: opts.width, height: opts.height, channels: 3 }, - }) - .jpeg({ quality: 100, chromaSubsampling: "4:4:4" }) - .toBuffer(), - }, - { - name: "webp", - mime: "image/webp", - make: (buf: Buffer, opts: { width: number; height: number }) => - sharp(buf, { - raw: { width: opts.width, height: opts.height, channels: 3 }, - }) - .webp({ quality: 100 }) - .toBuffer(), - }, - ] as const; + it( + "compresses common formats to jpeg under the cap", + { timeout: 15_000 }, + async () => { + const formats = [ + { + name: "png", + mime: "image/png", + make: (buf: Buffer, opts: { width: number; height: number }) => + sharp(buf, { + raw: { width: opts.width, height: opts.height, channels: 3 }, + }) + .png({ compressionLevel: 0 }) + .toBuffer(), + }, + { + name: "jpeg", + mime: "image/jpeg", + make: (buf: Buffer, opts: { width: number; height: number }) => + sharp(buf, { + raw: { width: opts.width, height: opts.height, channels: 3 }, + }) + .jpeg({ quality: 100, chromaSubsampling: "4:4:4" }) + .toBuffer(), + }, + { + name: "webp", + mime: "image/webp", + make: (buf: Buffer, opts: { width: number; height: number }) => + sharp(buf, { + raw: { width: opts.width, height: opts.height, channels: 3 }, + }) + .webp({ quality: 100 }) + .toBuffer(), + }, + ] as const; for (const fmt of formats) { // Force a small cap to ensure compression is exercised for every format.