diff --git a/src/auto-reply/reply.ts b/src/auto-reply/reply.ts index 86e99ebf4..9786a1613 100644 --- a/src/auto-reply/reply.ts +++ b/src/auto-reply/reply.ts @@ -243,6 +243,8 @@ export async function getReplyFromConfig( } } const finalArgv = argv; + const isClaudeInvocation = + finalArgv.length > 0 && path.basename(finalArgv[0]) === CLAUDE_BIN; logVerbose(`Running command auto-reply: ${finalArgv.join(" ")}`); const started = Date.now(); try { @@ -255,7 +257,7 @@ export async function getReplyFromConfig( if (stderr?.trim()) { logVerbose(`Command auto-reply stderr: ${stderr.trim()}`); } - if (reply.claudeOutputFormat === "json" && trimmed) { + if (trimmed && (reply.claudeOutputFormat === "json" || isClaudeInvocation)) { // Claude JSON mode: extract the human text for both logging and reply while keeping metadata. const parsed = parseClaudeJson(trimmed); if (parsed?.parsed && isVerbose()) { diff --git a/src/index.core.test.ts b/src/index.core.test.ts index 1515af09b..5694dce64 100644 --- a/src/index.core.test.ts +++ b/src/index.core.test.ts @@ -209,6 +209,34 @@ describe("config and templating", () => { expect(result).toBe("hello world"); }); + + it("parses Claude JSON output even without explicit claudeOutputFormat when using claude bin", async () => { + const runSpy = vi.spyOn(index, "runCommandWithTimeout").mockResolvedValue({ + stdout: '{"result":"Sure! What\'s up?"}\n', + stderr: "", + code: 0, + signal: null, + killed: false, + }); + const cfg = { + inbound: { + reply: { + mode: "command" as const, + command: ["claude", "{{Body}}"], + // No claudeOutputFormat set on purpose + }, + }, + }; + + const result = await index.getReplyFromConfig( + { Body: "hi", From: "+1", To: "+2" }, + undefined, + cfg, + runSpy, + ); + + expect(result).toBe("Sure! What's up?"); + }); }); describe("twilio interactions", () => { diff --git a/src/provider-web.test.ts b/src/provider-web.test.ts index 6d50f76a5..e4babd499 100644 --- a/src/provider-web.test.ts +++ b/src/provider-web.test.ts @@ -1,4 +1,5 @@ import { EventEmitter } from "node:events"; +import fsSync from "node:fs"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { MockBaileysSocket } from "../test/mocks/baileys.js"; import { createMockBaileys } from "../test/mocks/baileys.js"; @@ -30,6 +31,7 @@ import { createWaSocket, loginWeb, monitorWebInbox, + logWebSelfId, sendMessageWeb, waitForWaConnection, } from "./provider-web.js"; @@ -162,4 +164,28 @@ describe("provider-web", () => { await listener.close(); }); + + it("logWebSelfId prints cached E.164 when creds exist", () => { + const existsSpy = vi + .spyOn(fsSync, "existsSync") + .mockReturnValue(true as never); + const readSpy = vi + .spyOn(fsSync, "readFileSync") + .mockReturnValue( + JSON.stringify({ me: { id: "12345@s.whatsapp.net" } }), + ); + const runtime = { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn(), + }; + + logWebSelfId(runtime as never); + + expect(runtime.log).toHaveBeenCalledWith( + expect.stringContaining("+12345"), + ); + existsSpy.mockRestore(); + readSpy.mockRestore(); + }); }); diff --git a/src/provider-web.ts b/src/provider-web.ts index 3e5391d9a..68053f9ab 100644 --- a/src/provider-web.ts +++ b/src/provider-web.ts @@ -341,10 +341,10 @@ function readWebSelfId() { // Read the cached WhatsApp Web identity (jid + E.164) from disk if present. const credsPath = path.join(WA_WEB_AUTH_DIR, "creds.json"); try { - if (!fs.existsSync(credsPath)) { + if (!fsSync.existsSync(credsPath)) { return { e164: null, jid: null }; } - const raw = fs.readFileSync(credsPath, "utf-8"); + const raw = fsSync.readFileSync(credsPath, "utf-8"); const parsed = JSON.parse(raw) as { me?: { id?: string } } | undefined; const jid = parsed?.me?.id ?? null; const e164 = jid ? jidToE164(jid) : null; diff --git a/src/twilio/update-webhook.ts b/src/twilio/update-webhook.ts index 9c144b57d..b362e2088 100644 --- a/src/twilio/update-webhook.ts +++ b/src/twilio/update-webhook.ts @@ -1,7 +1,8 @@ -import { success, isVerbose, warn } from "../globals.js"; +import { isVerbose, success, warn } from "../globals.js"; +import { logError, logInfo } from "../logger.js"; import { readEnv } from "../env.js"; import { normalizeE164 } from "../utils.js"; -import type { RuntimeEnv } from "../runtime.js"; +import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; import { createClient } from "./client.js"; import type { TwilioSenderListClient, TwilioRequester } from "./types.js"; @@ -43,6 +44,7 @@ export async function setMessagingServiceWebhook( client: TwilioSenderListClient, url: string, method: "POST" | "GET", + runtime: RuntimeEnv = defaultRuntime, ): Promise { const msid = await findMessagingServiceSid(client); if (!msid) return false; @@ -53,10 +55,9 @@ export async function setMessagingServiceWebhook( }); const fetched = await client.messaging.v1.services(msid).fetch(); const stored = fetched?.inboundRequestUrl; - console.log( - success( - `✅ Messaging Service webhook set to ${stored ?? url} (service ${msid})`, - ), + logInfo( + `✅ Messaging Service webhook set to ${stored ?? url} (service ${msid})`, + runtime, ); return true; } catch { @@ -96,18 +97,19 @@ export async function updateWebhook( const storedUrl = fetched?.webhook?.callback_url || fetched?.webhook?.fallback_url; if (storedUrl) { - console.log(success(`✅ Twilio sender webhook set to ${storedUrl}`)); + logInfo(`✅ Twilio sender webhook set to ${storedUrl}`, runtime); return; } if (isVerbose()) - console.error( + logError( "Sender updated but webhook callback_url missing; will try fallbacks", + runtime, ); } catch (err) { if (isVerbose()) - console.error( - "channelsSenders request update failed, will try client helpers", - err, + logError( + `channelsSenders request update failed, will try client helpers: ${String(err)}`, + runtime, ); } @@ -127,18 +129,19 @@ export async function updateWebhook( const storedUrl = fetched?.webhook?.callback_url || fetched?.webhook?.fallback_url; if (storedUrl) { - console.log(success(`✅ Twilio sender webhook set to ${storedUrl}`)); + logInfo(`✅ Twilio sender webhook set to ${storedUrl}`, runtime); return; } if (isVerbose()) - console.error( + logError( "Form update succeeded but callback_url missing; will try helper fallback", + runtime, ); } catch (err) { if (isVerbose()) - console.error( - "Form channelsSenders update failed, will try helper fallback", - err, + logError( + `Form channelsSenders update failed, will try helper fallback: ${String(err)}`, + runtime, ); } @@ -154,18 +157,17 @@ export async function updateWebhook( .fetch(); const storedUrl = fetched?.webhook?.callback_url || fetched?.webhook?.fallback_url; - console.log( - success( - `✅ Twilio sender webhook set to ${storedUrl ?? url} (helper API)`, - ), + logInfo( + `✅ Twilio sender webhook set to ${storedUrl ?? url} (helper API)`, + runtime, ); return; } } catch (err) { if (isVerbose()) - console.error( - "channelsSenders helper update failed, will try phone number fallback", - err, + logError( + `channelsSenders helper update failed, will try phone number fallback: ${String(err)}`, + runtime, ); } @@ -177,14 +179,14 @@ export async function updateWebhook( smsUrl: url, smsMethod: method, }); - console.log(success(`✅ Phone webhook set to ${url} (number ${phoneSid})`)); + logInfo(`✅ Phone webhook set to ${url} (number ${phoneSid})`, runtime); return; } } catch (err) { if (isVerbose()) - console.error( - "Incoming phone number webhook update failed; no more fallbacks", - err, + logError( + `Incoming phone number webhook update failed; no more fallbacks: ${String(err)}`, + runtime, ); }