style: apply biome formatting
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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) =>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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, {
|
||||||
|
|||||||
@@ -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", () => {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user