style: apply biome formatting

This commit is contained in:
Peter Steinberger
2026-01-02 01:29:05 +01:00
parent 336048441c
commit c31070db24
13 changed files with 78 additions and 62 deletions

View File

@@ -438,11 +438,11 @@ export async function agentCommand(
? whatsappTarget ? whatsappTarget
: deliveryProvider === "discord" : deliveryProvider === "discord"
? discordTarget ? discordTarget
: deliveryProvider === "signal" : deliveryProvider === "signal"
? signalTarget ? signalTarget
: deliveryProvider === "imessage" : deliveryProvider === "imessage"
? imessageTarget ? imessageTarget
: undefined; : undefined;
const message = `Delivery failed (${deliveryProvider}${deliveryTarget ? ` to ${deliveryTarget}` : ""}): ${String(err)}`; const message = `Delivery failed (${deliveryProvider}${deliveryTarget ? ` to ${deliveryTarget}` : ""}): ${String(err)}`;
runtime.error?.(message); runtime.error?.(message);
if (!runtime.error) runtime.log(message); if (!runtime.error) runtime.log(message);

View File

@@ -102,7 +102,13 @@ export type HookMappingConfig = {
messageTemplate?: string; messageTemplate?: string;
textTemplate?: string; textTemplate?: string;
deliver?: boolean; deliver?: boolean;
channel?: "last" | "whatsapp" | "telegram" | "discord" | "signal" | "imessage"; channel?:
| "last"
| "whatsapp"
| "telegram"
| "discord"
| "signal"
| "imessage";
to?: string; to?: string;
thinking?: string; thinking?: string;
timeoutSeconds?: number; timeoutSeconds?: number;
@@ -940,11 +946,7 @@ const ClawdisSchema = z.object({
cliPath: z.string().optional(), cliPath: z.string().optional(),
dbPath: z.string().optional(), dbPath: z.string().optional(),
service: z service: z
.union([ .union([z.literal("imessage"), z.literal("sms"), z.literal("auto")])
z.literal("imessage"),
z.literal("sms"),
z.literal("auto"),
])
.optional(), .optional(),
region: z.string().optional(), region: z.string().optional(),
allowFrom: z.array(z.union([z.string(), z.number()])).optional(), allowFrom: z.array(z.union([z.string(), z.number()])).optional(),

View File

@@ -53,7 +53,9 @@ function normalizeReactionEmoji(raw: string) {
throw new Error("emoji required"); throw new Error("emoji required");
} }
const customMatch = trimmed.match(/^<a?:([^:>]+):(\d+)>$/); const customMatch = trimmed.match(/^<a?:([^:>]+):(\d+)>$/);
const identifier = customMatch ? `${customMatch[1]}:${customMatch[2]}` : trimmed; const identifier = customMatch
? `${customMatch[1]}:${customMatch[2]}`
: trimmed;
return encodeURIComponent(identifier); return encodeURIComponent(identifier);
} }

View File

@@ -3664,7 +3664,9 @@ describe("gateway server", () => {
ws, ws,
(o) => { (o) => {
if (o.type !== "event" || o.event !== "chat") return false; if (o.type !== "event" || o.event !== "chat") return false;
const payload = o.payload as { state?: unknown; runId?: unknown } | undefined; const payload = o.payload as
| { state?: unknown; runId?: unknown }
| undefined;
return payload?.state === "final" && payload.runId === "run-auto-1"; return payload?.state === "final" && payload.runId === "run-auto-1";
}, },
8000, 8000,

View File

@@ -74,12 +74,12 @@ import {
sendMessageDiscord, sendMessageDiscord,
} from "../discord/index.js"; } from "../discord/index.js";
import { type DiscordProbe, probeDiscord } from "../discord/probe.js"; import { type DiscordProbe, probeDiscord } from "../discord/probe.js";
import { isVerbose } from "../globals.js";
import { import {
monitorIMessageProvider, monitorIMessageProvider,
sendMessageIMessage, sendMessageIMessage,
} from "../imessage/index.js"; } from "../imessage/index.js";
import { probeIMessage, type IMessageProbe } from "../imessage/probe.js"; import { type IMessageProbe, probeIMessage } from "../imessage/probe.js";
import { isVerbose } from "../globals.js";
import { import {
clearAgentRunContext, clearAgentRunContext,
getAgentRunContext, getAgentRunContext,
@@ -1394,14 +1394,13 @@ export async function startGatewayServer(
? channelRaw ? channelRaw
: channelRaw === "imsg" : channelRaw === "imsg"
? "imessage" ? "imessage"
: channelRaw === undefined : channelRaw === undefined
? "last" ? "last"
: null; : null;
if (channel === null) { if (channel === null) {
return { return {
ok: false, ok: false,
error: error: "channel must be last|whatsapp|telegram|discord|signal|imessage",
"channel must be last|whatsapp|telegram|discord|signal|imessage",
}; };
} }
const toRaw = payload.to; const toRaw = payload.to;
@@ -4331,7 +4330,8 @@ export async function startGatewayServer(
const imessageCfg = cfg.imessage; const imessageCfg = cfg.imessage;
const imessageEnabled = imessageCfg?.enabled !== false; const imessageEnabled = imessageCfg?.enabled !== false;
const imessageConfigured = Boolean(imessageCfg) && imessageEnabled; const imessageConfigured =
Boolean(imessageCfg) && imessageEnabled;
let imessageProbe: IMessageProbe | undefined; let imessageProbe: IMessageProbe | undefined;
let imessageLastProbeAt: number | null = null; let imessageLastProbeAt: number | null = null;
if (probe && imessageConfigured) { if (probe && imessageConfigured) {
@@ -6788,9 +6788,7 @@ export async function startGatewayServer(
await Promise.allSettled( await Promise.allSettled(
[whatsappTask, telegramTask, signalTask, imessageTask].filter( [whatsappTask, telegramTask, signalTask, imessageTask].filter(
Boolean, Boolean,
) as Array< ) as Array<Promise<unknown>>,
Promise<unknown>
>,
); );
await new Promise<void>((resolve) => wss.close(() => resolve())); await new Promise<void>((resolve) => wss.close(() => resolve()));
await new Promise<void>((resolve, reject) => await new Promise<void>((resolve, reject) =>

View File

@@ -1,4 +1,4 @@
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
import { createInterface, type Interface } from "node:readline"; import { createInterface, type Interface } from "node:readline";
import type { RuntimeEnv } from "../runtime.js"; import type { RuntimeEnv } from "../runtime.js";
@@ -51,7 +51,9 @@ export class IMessageRpcClient {
constructor(opts: IMessageRpcClientOptions = {}) { constructor(opts: IMessageRpcClientOptions = {}) {
this.cliPath = opts.cliPath?.trim() || "imsg"; this.cliPath = opts.cliPath?.trim() || "imsg";
this.dbPath = opts.dbPath?.trim() ? resolveUserPath(opts.dbPath) : undefined; this.dbPath = opts.dbPath?.trim()
? resolveUserPath(opts.dbPath)
: undefined;
this.runtime = opts.runtime; this.runtime = opts.runtime;
this.onNotification = opts.onNotification; this.onNotification = opts.onNotification;
this.closed = new Promise((resolve) => { this.closed = new Promise((resolve) => {
@@ -166,7 +168,7 @@ export class IMessageRpcClient {
let parsed: IMessageRpcResponse<unknown>; let parsed: IMessageRpcResponse<unknown>;
try { try {
parsed = JSON.parse(line) as IMessageRpcResponse<unknown>; parsed = JSON.parse(line) as IMessageRpcResponse<unknown>;
} catch (err) { } catch (_err) {
this.runtime?.error?.(`imsg rpc: failed to parse ${line}`); this.runtime?.error?.(`imsg rpc: failed to parse ${line}`);
return; return;
} }

View File

@@ -9,7 +9,9 @@ const replyMock = vi.fn();
const updateLastRouteMock = vi.fn(); const updateLastRouteMock = vi.fn();
let config: Record<string, unknown> = {}; let config: Record<string, unknown> = {};
let notificationHandler: ((msg: { method: string; params?: unknown }) => void) | undefined; let notificationHandler:
| ((msg: { method: string; params?: unknown }) => void)
| undefined;
let closeResolve: (() => void) | undefined; let closeResolve: (() => void) | undefined;
vi.mock("../config/config.js", () => ({ vi.mock("../config/config.js", () => ({
@@ -30,24 +32,27 @@ vi.mock("../config/sessions.js", () => ({
})); }));
vi.mock("./client.js", () => ({ vi.mock("./client.js", () => ({
createIMessageRpcClient: vi.fn(async (opts: { onNotification?: typeof notificationHandler }) => { createIMessageRpcClient: vi.fn(
notificationHandler = opts.onNotification; async (opts: { onNotification?: typeof notificationHandler }) => {
return { notificationHandler = opts.onNotification;
request: (...args: unknown[]) => requestMock(...args), return {
waitForClose: () => request: (...args: unknown[]) => requestMock(...args),
new Promise<void>((resolve) => { waitForClose: () =>
closeResolve = resolve; new Promise<void>((resolve) => {
}), closeResolve = resolve;
stop: (...args: unknown[]) => stopMock(...args), }),
}; stop: (...args: unknown[]) => stopMock(...args),
}), };
},
),
})); }));
const flush = () => new Promise((resolve) => setTimeout(resolve, 0)); const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
async function waitForSubscribe() { async function waitForSubscribe() {
for (let i = 0; i < 5; i += 1) { for (let i = 0; i < 5; i += 1) {
if (requestMock.mock.calls.some((call) => call[0] === "watch.subscribe")) return; if (requestMock.mock.calls.some((call) => call[0] === "watch.subscribe"))
return;
await flush(); await flush();
} }
} }
@@ -62,7 +67,8 @@ beforeEach(() => {
}, },
}; };
requestMock.mockReset().mockImplementation((method: string) => { requestMock.mockReset().mockImplementation((method: string) => {
if (method === "watch.subscribe") return Promise.resolve({ subscription: 1 }); if (method === "watch.subscribe")
return Promise.resolve({ subscription: 1 });
return Promise.resolve({}); return Promise.resolve({});
}); });
stopMock.mockReset().mockResolvedValue(undefined); stopMock.mockReset().mockResolvedValue(undefined);

View File

@@ -176,15 +176,18 @@ export async function monitorIMessageProvider(
return; return;
} }
const attachments = includeAttachments ? message.attachments ?? [] : []; const attachments = includeAttachments ? (message.attachments ?? []) : [];
const firstAttachment = attachments?.find( const firstAttachment = attachments?.find(
(entry) => entry?.original_path && !entry?.missing, (entry) => entry?.original_path && !entry?.missing,
); );
const mediaPath = firstAttachment?.original_path ?? undefined; const mediaPath = firstAttachment?.original_path ?? undefined;
const mediaType = firstAttachment?.mime_type ?? undefined; const mediaType = firstAttachment?.mime_type ?? undefined;
const kind = mediaKindFromMime(mediaType ?? undefined); const kind = mediaKindFromMime(mediaType ?? undefined);
const placeholder = const placeholder = kind
kind ? `<media:${kind}>` : attachments?.length ? "<media:attachment>" : ""; ? `<media:${kind}>`
: attachments?.length
? "<media:attachment>"
: "";
const bodyText = messageText || placeholder; const bodyText = messageText || placeholder;
if (!bodyText) return; if (!bodyText) return;
@@ -279,7 +282,9 @@ export async function monitorIMessageProvider(
const abort = opts.abortSignal; const abort = opts.abortSignal;
const onAbort = () => { const onAbort = () => {
if (subscriptionId) { if (subscriptionId) {
void client.request("watch.unsubscribe", { subscription: subscriptionId }); void client.request("watch.unsubscribe", {
subscription: subscriptionId,
});
} }
void client.stop(); void client.stop();
}; };

View File

@@ -7,9 +7,7 @@ export type IMessageProbe = {
error?: string | null; error?: string | null;
}; };
export async function probeIMessage( export async function probeIMessage(timeoutMs = 2000): Promise<IMessageProbe> {
timeoutMs = 2000,
): Promise<IMessageProbe> {
const cfg = loadConfig(); const cfg = loadConfig();
const cliPath = cfg.imessage?.cliPath?.trim() || "imsg"; const cliPath = cfg.imessage?.cliPath?.trim() || "imsg";
const dbPath = cfg.imessage?.dbPath?.trim(); const dbPath = cfg.imessage?.dbPath?.trim();

View File

@@ -5,8 +5,8 @@ import { loadWebMedia } from "../web/media.js";
import { createIMessageRpcClient, type IMessageRpcClient } from "./client.js"; import { createIMessageRpcClient, type IMessageRpcClient } from "./client.js";
import { import {
formatIMessageChatTarget, formatIMessageChatTarget,
parseIMessageTarget,
type IMessageService, type IMessageService,
parseIMessageTarget,
} from "./targets.js"; } from "./targets.js";
export type IMessageSendOpts = { export type IMessageSendOpts = {
@@ -38,9 +38,7 @@ function resolveDbPath(explicit?: string): string | undefined {
function resolveService(explicit?: IMessageService): IMessageService { function resolveService(explicit?: IMessageService): IMessageService {
const cfg = loadConfig(); const cfg = loadConfig();
return ( return (
explicit || explicit || (cfg.imessage?.service as IMessageService | undefined) || "auto"
(cfg.imessage?.service as IMessageService | undefined) ||
"auto"
); );
} }
@@ -85,7 +83,8 @@ export async function sendMessageIMessage(
filePath = resolved.path; filePath = resolved.path;
if (!message.trim()) { if (!message.trim()) {
const kind = mediaKindFromMime(resolved.contentType ?? undefined); const kind = mediaKindFromMime(resolved.contentType ?? undefined);
if (kind) message = kind === "image" ? "<media:image>" : `<media:${kind}>`; if (kind)
message = kind === "image" ? "<media:image>" : `<media:${kind}>`;
} }
} }
@@ -110,7 +109,8 @@ export async function sendMessageIMessage(
params.to = target.to; params.to = target.to;
} }
const client = opts.client ?? (await createIMessageRpcClient({ cliPath, dbPath })); const client =
opts.client ?? (await createIMessageRpcClient({ cliPath, dbPath }));
const shouldClose = !opts.client; const shouldClose = !opts.client;
try { try {
const result = await client.request<{ ok?: boolean }>("send", params, { const result = await client.request<{ ok?: boolean }>("send", params, {

View File

@@ -27,9 +27,7 @@ describe("imessage targets", () => {
expect(normalizeIMessageHandle("Name@Example.com")).toBe( expect(normalizeIMessageHandle("Name@Example.com")).toBe(
"name@example.com", "name@example.com",
); );
expect(normalizeIMessageHandle(" +1 (555) 222-3333 ")).toBe( expect(normalizeIMessageHandle(" +1 (555) 222-3333 ")).toBe("+15552223333");
"+15552223333",
);
}); });
it("checks allowFrom against chat_id", () => { it("checks allowFrom against chat_id", () => {

View File

@@ -35,9 +35,12 @@ export function normalizeIMessageHandle(raw: string): string {
const trimmed = raw.trim(); const trimmed = raw.trim();
if (!trimmed) return ""; if (!trimmed) return "";
const lowered = trimmed.toLowerCase(); const lowered = trimmed.toLowerCase();
if (lowered.startsWith("imessage:")) return normalizeIMessageHandle(trimmed.slice(9)); if (lowered.startsWith("imessage:"))
if (lowered.startsWith("sms:")) return normalizeIMessageHandle(trimmed.slice(4)); return normalizeIMessageHandle(trimmed.slice(9));
if (lowered.startsWith("auto:")) return normalizeIMessageHandle(trimmed.slice(5)); if (lowered.startsWith("sms:"))
return normalizeIMessageHandle(trimmed.slice(4));
if (lowered.startsWith("auto:"))
return normalizeIMessageHandle(trimmed.slice(5));
if (trimmed.includes("@")) return trimmed.toLowerCase(); if (trimmed.includes("@")) return trimmed.toLowerCase();
const normalized = normalizeE164(trimmed); const normalized = normalizeE164(trimmed);
if (normalized) return normalized; if (normalized) return normalized;

View File

@@ -1,10 +1,10 @@
import { describe, expect, test } from "vitest"; import { describe, expect, test } from "vitest";
import { import {
clearAgentRunContext,
emitAgentEvent, emitAgentEvent,
getAgentRunContext,
onAgentEvent, onAgentEvent,
registerAgentRunContext, registerAgentRunContext,
getAgentRunContext,
clearAgentRunContext,
resetAgentRunContextForTest, resetAgentRunContextForTest,
} from "./agent-events.js"; } from "./agent-events.js";