From 4a35bcec21c37237ad57e7d89b7941c67a9198ed Mon Sep 17 00:00:00 2001 From: "Eng. Juan Combetto" Date: Fri, 5 Dec 2025 00:36:41 +0900 Subject: [PATCH] fix: resolve lint errors (unused vars, imports, formatting) - Prefix unused test variables with underscore - Remove unused piSpec import and idleMs class member - Fix import ordering and code formatting --- src/auto-reply/command-reply.ts | 175 ++++++++++++++++---------------- src/auto-reply/reply.ts | 14 ++- src/config/sessions.test.ts | 6 +- src/index.core.test.ts | 6 +- src/index.ts | 2 +- src/infra/restart.ts | 3 +- src/process/tau-rpc.ts | 10 +- src/web/auto-reply.ts | 50 ++++----- src/web/inbound.ts | 4 +- 9 files changed, 144 insertions(+), 126 deletions(-) diff --git a/src/auto-reply/command-reply.ts b/src/auto-reply/command-reply.ts index 1f6cae60d..6eecb945a 100644 --- a/src/auto-reply/command-reply.ts +++ b/src/auto-reply/command-reply.ts @@ -16,8 +16,8 @@ import { formatToolAggregate, shortenMeta, shortenPath, - TOOL_RESULT_FLUSH_COUNT, TOOL_RESULT_DEBOUNCE_MS, + TOOL_RESULT_FLUSH_COUNT, } from "./tool-meta.js"; import type { ReplyPayload } from "./types.js"; @@ -345,7 +345,11 @@ export async function runCommandReply( // Tau (pi agent) needs --continue to reload prior messages when resuming. // Without it, pi starts from a blank state even though we pass the session file path. - if (agentKind === "pi" && !isNewSession && !sessionArgList.includes("--continue")) { + if ( + agentKind === "pi" && + !isNewSession && + !sessionArgList.includes("--continue") + ) { sessionArgList.push("--continue"); } @@ -433,10 +437,7 @@ export async function runCommandReply( } }; let lastStreamedAssistant: string | undefined; - const streamAssistant = (msg?: { - role?: string; - content?: unknown[]; - }) => { + const streamAssistant = (msg?: { role?: string; content?: unknown[] }) => { if (!onPartialReply || msg?.role !== "assistant") return; const textBlocks = Array.isArray(msg.content) ? (msg.content as Array<{ type?: string; text?: string }>) @@ -478,68 +479,37 @@ export async function runCommandReply( cwd: reply.cwd, prompt: body, timeoutMs, - onEvent: - onPartialReply - ? (line: string) => { - try { - const ev = JSON.parse(line) as { - type?: string; - message?: { - role?: string; - content?: unknown[]; - details?: Record; - arguments?: Record; - toolCallId?: string; - tool_call_id?: string; - toolName?: string; - name?: string; - }; + onEvent: onPartialReply + ? (line: string) => { + try { + const ev = JSON.parse(line) as { + type?: string; + message?: { + role?: string; + content?: unknown[]; + details?: Record; + arguments?: Record; toolCallId?: string; + tool_call_id?: string; toolName?: string; - args?: Record; + name?: string; }; - // Capture metadata as soon as the tool starts (from args). - if (ev.type === "tool_execution_start") { - const toolName = ev.toolName; - const meta = inferToolMeta({ - toolName, - name: ev.toolName, - arguments: ev.args, - }); - if (ev.toolCallId) { - toolMetaById.set(ev.toolCallId, meta); - } - if (meta) { - if (pendingToolName && toolName && toolName !== pendingToolName) { - flushPendingTool(); - } - if (!pendingToolName) pendingToolName = toolName; - pendingMetas.push(meta); - if ( - TOOL_RESULT_FLUSH_COUNT > 0 && - pendingMetas.length >= TOOL_RESULT_FLUSH_COUNT - ) { - flushPendingTool(); - } else { - if (pendingTimer) clearTimeout(pendingTimer); - pendingTimer = setTimeout( - flushPendingTool, - TOOL_RESULT_DEBOUNCE_MS, - ); - } - } + toolCallId?: string; + toolName?: string; + args?: Record; + }; + // Capture metadata as soon as the tool starts (from args). + if (ev.type === "tool_execution_start") { + const toolName = ev.toolName; + const meta = inferToolMeta({ + toolName, + name: ev.toolName, + arguments: ev.args, + }); + if (ev.toolCallId) { + toolMetaById.set(ev.toolCallId, meta); } - if ( - (ev.type === "message" || ev.type === "message_end") && - ev.message?.role === "tool_result" && - Array.isArray(ev.message.content) - ) { - const toolName = inferToolName(ev.message); - const toolCallId = - ev.message.toolCallId ?? ev.message.tool_call_id; - const meta = - inferToolMeta(ev.message) ?? - (toolCallId ? toolMetaById.get(toolCallId) : undefined); + if (meta) { if ( pendingToolName && toolName && @@ -548,32 +518,66 @@ export async function runCommandReply( flushPendingTool(); } if (!pendingToolName) pendingToolName = toolName; - if (meta) pendingMetas.push(meta); + pendingMetas.push(meta); if ( TOOL_RESULT_FLUSH_COUNT > 0 && pendingMetas.length >= TOOL_RESULT_FLUSH_COUNT ) { flushPendingTool(); - return; + } else { + if (pendingTimer) clearTimeout(pendingTimer); + pendingTimer = setTimeout( + flushPendingTool, + TOOL_RESULT_DEBOUNCE_MS, + ); } - if (pendingTimer) clearTimeout(pendingTimer); - pendingTimer = setTimeout( - flushPendingTool, - TOOL_RESULT_DEBOUNCE_MS, - ); } - if ( - ev.type === "message_end" || - ev.type === "message_update" || - ev.type === "message" - ) { - streamAssistant(ev.message); - } - } catch { - // ignore malformed lines } + if ( + (ev.type === "message" || ev.type === "message_end") && + ev.message?.role === "tool_result" && + Array.isArray(ev.message.content) + ) { + const toolName = inferToolName(ev.message); + const toolCallId = + ev.message.toolCallId ?? ev.message.tool_call_id; + const meta = + inferToolMeta(ev.message) ?? + (toolCallId ? toolMetaById.get(toolCallId) : undefined); + if ( + pendingToolName && + toolName && + toolName !== pendingToolName + ) { + flushPendingTool(); + } + if (!pendingToolName) pendingToolName = toolName; + if (meta) pendingMetas.push(meta); + if ( + TOOL_RESULT_FLUSH_COUNT > 0 && + pendingMetas.length >= TOOL_RESULT_FLUSH_COUNT + ) { + flushPendingTool(); + return; + } + if (pendingTimer) clearTimeout(pendingTimer); + pendingTimer = setTimeout( + flushPendingTool, + TOOL_RESULT_DEBOUNCE_MS, + ); + } + if ( + ev.type === "message_end" || + ev.type === "message_update" || + ev.type === "message" + ) { + streamAssistant(ev.message); + } + } catch { + // ignore malformed lines } - : undefined, + } + : undefined, }); flushPendingTool(); return rpcResult; @@ -610,10 +614,10 @@ export async function runCommandReply( type ReplyItem = { text: string; media?: string[] }; const replyItems: ReplyItem[] = []; - const includeToolResultsInline = + const includeToolResultsInline = verboseLevel === "on" && !onPartialReply && parsedToolResults.length > 0; - if (includeToolResultsInline) { + if (includeToolResultsInline) { const aggregated = parsedToolResults.reduce< { toolName?: string; metas: string[]; previews: string[] }[] >((acc, tr) => { @@ -647,7 +651,8 @@ export async function runCommandReply( const formatPreview = (texts: string[]) => { const joined = texts.join(" ").trim(); if (!joined) return ""; - const clipped = joined.length > 120 ? `${joined.slice(0, 117)}…` : joined; + const clipped = + joined.length > 120 ? `${joined.slice(0, 117)}…` : joined; return ` — “${clipped}”`; }; @@ -662,7 +667,7 @@ export async function runCommandReply( media: mediaFound?.length ? mediaFound : undefined, }); } - } + } for (const t of parsedTexts) { const { text: cleanedText, mediaUrls: mediaFound } = diff --git a/src/auto-reply/reply.ts b/src/auto-reply/reply.ts index 1e191a1bc..a78cad5e8 100644 --- a/src/auto-reply/reply.ts +++ b/src/auto-reply/reply.ts @@ -11,12 +11,12 @@ import { saveSessionStore, } from "../config/sessions.js"; import { info, isVerbose, logVerbose } from "../globals.js"; +import { triggerWarelayRestart } from "../infra/restart.js"; import { ensureMediaHosted } from "../media/host.js"; import { runCommandWithTimeout } from "../process/exec.js"; import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; import type { TwilioRequester } from "../twilio/types.js"; import { sendTypingIndicator } from "../twilio/typing.js"; -import { triggerWarelayRestart } from "../infra/restart.js"; import { chunkText } from "./chunk.js"; import { runCommandReply } from "./command-reply.js"; import { @@ -358,7 +358,13 @@ export async function getReplyFromConfig( await saveSessionStore(storePath, sessionStore); } // If verbose directive is also present, persist it too. - if (hasVerboseDirective && inlineVerbose && sessionEntry && sessionStore && sessionKey) { + if ( + hasVerboseDirective && + inlineVerbose && + sessionEntry && + sessionStore && + sessionKey + ) { if (inlineVerbose === "off") { delete sessionEntry.verboseLevel; } else { @@ -431,9 +437,7 @@ export async function getReplyFromConfig( const to = (ctx.To ?? "").replace(/^whatsapp:/, ""); const isSamePhone = from && to && from === to; const abortKey = sessionKey ?? (from || undefined) ?? (to || undefined); - const rawBodyNormalized = ( - sessionCtx.BodyStripped ?? sessionCtx.Body ?? "" - ) + const rawBodyNormalized = (sessionCtx.BodyStripped ?? sessionCtx.Body ?? "") .trim() .toLowerCase(); diff --git a/src/config/sessions.test.ts b/src/config/sessions.test.ts index a52738541..0743d1503 100644 --- a/src/config/sessions.test.ts +++ b/src/config/sessions.test.ts @@ -18,8 +18,8 @@ describe("sessions", () => { }); it("keeps group chats distinct", () => { - expect( - deriveSessionKey("per-sender", { From: "12345-678@g.us" }), - ).toBe("group:12345-678@g.us"); + expect(deriveSessionKey("per-sender", { From: "12345-678@g.us" })).toBe( + "group:12345-678@g.us", + ); }); }); diff --git a/src/index.core.test.ts b/src/index.core.test.ts index 51741cbf0..5793a3973 100644 --- a/src/index.core.test.ts +++ b/src/index.core.test.ts @@ -678,7 +678,7 @@ describe("config and templating", () => { }, }; - const ack = await index.getReplyFromConfig( + const _ack = await index.getReplyFromConfig( { Body: "/v:on", From: "+1", To: "+2" }, undefined, cfg, @@ -979,7 +979,7 @@ describe("config and templating", () => { const batchBody = "[Current message - respond to this]\nPeter: @Clawd UK /thinking medium /v on"; - const ack = await index.getReplyFromConfig( + const _ack = await index.getReplyFromConfig( { Body: batchBody, From: "group:456@g.us", @@ -1000,7 +1000,7 @@ describe("config and templating", () => { const persisted = JSON.parse( await fs.promises.readFile(storePath, "utf-8"), ) as Record; - const entry = Object.values(persisted)[0] as { + const _entry = Object.values(persisted)[0] as { thinkingLevel?: string; verboseLevel?: string; }; diff --git a/src/index.ts b/src/index.ts index dd4a76ea2..00e2d0487 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,6 @@ import { autoReplyIfConfigured, getReplyFromConfig, } from "./auto-reply/reply.js"; -import { enableConsoleCapture } from "./logging.js"; import { applyTemplate } from "./auto-reply/templating.js"; import { createDefaultDeps, monitorTwilio } from "./cli/deps.js"; import { promptYesNo } from "./cli/prompt.js"; @@ -33,6 +32,7 @@ import { ensureTailscaledInstalled, getTailnetHostname, } from "./infra/tailscale.js"; +import { enableConsoleCapture } from "./logging.js"; import { runCommandWithTimeout, runExec } from "./process/exec.js"; import { monitorWebProvider } from "./provider-web.js"; import { createClient } from "./twilio/client.js"; diff --git a/src/infra/restart.ts b/src/infra/restart.ts index cd6cfc720..b213b3fe1 100644 --- a/src/infra/restart.ts +++ b/src/infra/restart.ts @@ -4,7 +4,8 @@ const DEFAULT_LAUNCHD_LABEL = "com.steipete.warelay"; export function triggerWarelayRestart(): void { const label = process.env.WARELAY_LAUNCHD_LABEL || DEFAULT_LAUNCHD_LABEL; - const uid = typeof process.getuid === "function" ? process.getuid() : undefined; + const uid = + typeof process.getuid === "function" ? process.getuid() : undefined; const target = uid !== undefined ? `gui/${uid}/${label}` : label; const child = spawn("launchctl", ["kickstart", "-k", target], { detached: true, diff --git a/src/process/tau-rpc.ts b/src/process/tau-rpc.ts index 50d577a65..5b62f5b9d 100644 --- a/src/process/tau-rpc.ts +++ b/src/process/tau-rpc.ts @@ -1,8 +1,6 @@ import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process"; import readline from "node:readline"; -import { piSpec } from "../agents/pi.js"; - type TauRpcOptions = { argv: string[]; cwd?: string; @@ -24,7 +22,6 @@ class TauRpcClient { private stderr = ""; private buffer: string[] = []; private idleTimer: NodeJS.Timeout | null = null; - private readonly idleMs = 120; private pending: | { resolve: (r: TauRpcResult) => void; @@ -58,7 +55,12 @@ class TauRpcClient { const out = this.buffer.join("\n"); clearTimeout(pending.timer); // Treat process exit as completion with whatever output we captured. - pending.resolve({ stdout: out, stderr: this.stderr, code: code ?? 0, signal }); + pending.resolve({ + stdout: out, + stderr: this.stderr, + code: code ?? 0, + signal, + }); } this.dispose(); }); diff --git a/src/web/auto-reply.ts b/src/web/auto-reply.ts index 4149d4223..2eb4d5dc7 100644 --- a/src/web/auto-reply.ts +++ b/src/web/auto-reply.ts @@ -84,13 +84,15 @@ function buildMentionConfig(cfg: ReturnType): MentionConfig { const gc = cfg.inbound?.groupChat; const requireMention = gc?.requireMention !== false; // default true const mentionRegexes = - gc?.mentionPatterns?.map((p) => { - try { - return new RegExp(p, "i"); - } catch { - return null; - } - }).filter((r): r is RegExp => Boolean(r)) ?? []; + gc?.mentionPatterns + ?.map((p) => { + try { + return new RegExp(p, "i"); + } catch { + return null; + } + }) + .filter((r): r is RegExp => Boolean(r)) ?? []; return { requireMention, mentionRegexes }; } @@ -728,7 +730,7 @@ export async function monitorWebProvider( const senderLabel = latest.senderName && latest.senderE164 ? `${latest.senderName} (${latest.senderE164})` - : latest.senderName ?? latest.senderE164 ?? "Unknown"; + : (latest.senderName ?? latest.senderE164 ?? "Unknown"); combinedBody = `${combinedBody}\\n[from: ${senderLabel}]`; // Clear stored history after using it groupHistories.set(conversationId, []); @@ -834,7 +836,7 @@ export async function monitorWebProvider( const fromDisplay = latest.chatType === "group" ? conversationId - : latest.from ?? "unknown"; + : (latest.from ?? "unknown"); if (isVerbose()) { console.log( success( @@ -850,24 +852,26 @@ export async function monitorWebProvider( } } catch (err) { console.error( - danger(`Failed sending web auto-reply to ${latest.from ?? conversationId}: ${String(err)}`), + danger( + `Failed sending web auto-reply to ${latest.from ?? conversationId}: ${String(err)}`, + ), ); } } - }; + }; - const enqueueBatch = async (msg: WebInboundMsg) => { - const key = msg.conversationId ?? msg.from; - const bucket = pendingBatches.get(key) ?? { messages: [] }; - bucket.messages.push(msg); - pendingBatches.set(key, bucket); - if (getQueueSize() === 0) { - await processBatch(key); - } else { - bucket.timer = - bucket.timer ?? setTimeout(() => void processBatch(key), 150); - } - }; + const enqueueBatch = async (msg: WebInboundMsg) => { + const key = msg.conversationId ?? msg.from; + const bucket = pendingBatches.get(key) ?? { messages: [] }; + bucket.messages.push(msg); + pendingBatches.set(key, bucket); + if (getQueueSize() === 0) { + await processBatch(key); + } else { + bucket.timer = + bucket.timer ?? setTimeout(() => void processBatch(key), 150); + } + }; const listener = await (listenerFactory ?? monitorWebInbox)({ verbose, diff --git a/src/web/inbound.ts b/src/web/inbound.ts index 53ad741f2..8b6d7eddb 100644 --- a/src/web/inbound.ts +++ b/src/web/inbound.ts @@ -339,7 +339,9 @@ export async function monitorWebInbox(options: { } as const; } -function unwrapMessage(message: proto.IMessage | undefined): proto.IMessage | undefined { +function unwrapMessage( + message: proto.IMessage | undefined, +): proto.IMessage | undefined { if (!message) return undefined; if (message.ephemeralMessage?.message) { return unwrapMessage(message.ephemeralMessage.message as proto.IMessage);