Skip to content

Commit 78f8a29

Browse files
yudshjsteipete
authored andcommitted
fix(imessage): unify timeout configuration with configurable probeTimeoutMs
- Add probeTimeoutMs config option to channels.imessage - Export DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS constant (10s) from probe.ts - Propagate timeout config through all iMessage probe/RPC operations - Fix hardcoded 2000ms timeouts that were too short for SSH connections Closes: timeout issues when using SSH wrapper scripts (imsg-ssh)
1 parent 78fd194 commit 78f8a29

File tree

4 files changed

+20
-7
lines changed

4 files changed

+20
-7
lines changed

src/config/types.imessage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export type IMessageAccountConfig = {
5252
includeAttachments?: boolean;
5353
/** Max outbound media size in MB. */
5454
mediaMaxMb?: number;
55+
/** Timeout for probe/RPC operations in milliseconds (default: 10000). */
56+
probeTimeoutMs?: number;
5557
/** Outbound text chunk size (chars). Default: 4000. */
5658
textChunkLimit?: number;
5759
/** Chunking mode: "length" (default) splits by size; "newline" splits on every newline. */

src/imessage/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ export class IMessageRpcClient {
149149
params: params ?? {},
150150
};
151151
const line = `${JSON.stringify(payload)}\n`;
152+
// Default timeout matches DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS from probe.ts
152153
const timeoutMs = opts?.timeoutMs ?? 10_000;
153154

154155
const response = new Promise<T>((resolve, reject) => {

src/imessage/monitor/monitor-provider.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import { resolveAgentRoute } from "../../routing/resolve-route.js";
4545
import { truncateUtf16Safe } from "../../utils.js";
4646
import { resolveIMessageAccount } from "../accounts.js";
4747
import { createIMessageRpcClient } from "../client.js";
48-
import { probeIMessage } from "../probe.js";
48+
import { DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS, probeIMessage } from "../probe.js";
4949
import { sendMessageIMessage } from "../send.js";
5050
import {
5151
formatIMessageChatTarget,
@@ -139,6 +139,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
139139
const mediaMaxBytes = (opts.mediaMaxMb ?? imessageCfg.mediaMaxMb ?? 16) * 1024 * 1024;
140140
const cliPath = opts.cliPath ?? imessageCfg.cliPath ?? "imsg";
141141
const dbPath = opts.dbPath ?? imessageCfg.dbPath;
142+
const probeTimeoutMs = imessageCfg.probeTimeoutMs ?? DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS;
142143

143144
// Resolve remoteHost: explicit config, or auto-detect from SSH wrapper script
144145
let remoteHost = imessageCfg.remoteHost;
@@ -618,7 +619,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
618619
abortSignal: opts.abortSignal,
619620
runtime,
620621
check: async () => {
621-
const probe = await probeIMessage(2000, { cliPath, dbPath, runtime });
622+
const probe = await probeIMessage(probeTimeoutMs, { cliPath, dbPath, runtime });
622623
if (probe.ok) {
623624
return { ok: true };
624625
}

src/imessage/probe.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { loadConfig } from "../config/config.js";
44
import { runCommandWithTimeout } from "../process/exec.js";
55
import { createIMessageRpcClient } from "./client.js";
66

7+
/** Default timeout for iMessage probe operations (10 seconds). */
8+
export const DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS = 10_000;
9+
710
export type IMessageProbe = {
811
ok: boolean;
912
error?: string | null;
@@ -24,13 +27,13 @@ type RpcSupportResult = {
2427

2528
const rpcSupportCache = new Map<string, RpcSupportResult>();
2629

27-
async function probeRpcSupport(cliPath: string): Promise<RpcSupportResult> {
30+
async function probeRpcSupport(cliPath: string, timeoutMs: number): Promise<RpcSupportResult> {
2831
const cached = rpcSupportCache.get(cliPath);
2932
if (cached) {
3033
return cached;
3134
}
3235
try {
33-
const result = await runCommandWithTimeout([cliPath, "rpc", "--help"], { timeoutMs: 2000 });
36+
const result = await runCommandWithTimeout([cliPath, "rpc", "--help"], { timeoutMs });
3437
const combined = `${result.stdout}\n${result.stderr}`.trim();
3538
const normalized = combined.toLowerCase();
3639
if (normalized.includes("unknown command") && normalized.includes("rpc")) {
@@ -57,18 +60,24 @@ async function probeRpcSupport(cliPath: string): Promise<RpcSupportResult> {
5760
}
5861

5962
export async function probeIMessage(
60-
timeoutMs = 2000,
63+
timeoutMs = DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS,
6164
opts: IMessageProbeOptions = {},
6265
): Promise<IMessageProbe> {
6366
const cfg = opts.cliPath || opts.dbPath ? undefined : loadConfig();
6467
const cliPath = opts.cliPath?.trim() || cfg?.channels?.imessage?.cliPath?.trim() || "imsg";
6568
const dbPath = opts.dbPath?.trim() || cfg?.channels?.imessage?.dbPath?.trim();
69+
// Read probeTimeoutMs from config if not explicitly provided
70+
const effectiveTimeout =
71+
timeoutMs !== DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS
72+
? timeoutMs
73+
: cfg?.channels?.imessage?.probeTimeoutMs ?? DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS;
74+
6675
const detected = await detectBinary(cliPath);
6776
if (!detected) {
6877
return { ok: false, error: `imsg not found (${cliPath})` };
6978
}
7079

71-
const rpcSupport = await probeRpcSupport(cliPath);
80+
const rpcSupport = await probeRpcSupport(cliPath, effectiveTimeout);
7281
if (!rpcSupport.supported) {
7382
return {
7483
ok: false,
@@ -83,7 +92,7 @@ export async function probeIMessage(
8392
runtime: opts.runtime,
8493
});
8594
try {
86-
await client.request("chats.list", { limit: 1 }, { timeoutMs });
95+
await client.request("chats.list", { limit: 1 }, { timeoutMs: effectiveTimeout });
8796
return { ok: true };
8897
} catch (err) {
8998
return { ok: false, error: String(err) };

0 commit comments

Comments
 (0)