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)
This commit is contained in:
committed by
Peter Steinberger
parent
78fd194722
commit
78f8a29071
@@ -52,6 +52,8 @@ export type IMessageAccountConfig = {
|
||||
includeAttachments?: boolean;
|
||||
/** Max outbound media size in MB. */
|
||||
mediaMaxMb?: number;
|
||||
/** Timeout for probe/RPC operations in milliseconds (default: 10000). */
|
||||
probeTimeoutMs?: number;
|
||||
/** Outbound text chunk size (chars). Default: 4000. */
|
||||
textChunkLimit?: number;
|
||||
/** Chunking mode: "length" (default) splits by size; "newline" splits on every newline. */
|
||||
|
||||
@@ -149,6 +149,7 @@ export class IMessageRpcClient {
|
||||
params: params ?? {},
|
||||
};
|
||||
const line = `${JSON.stringify(payload)}\n`;
|
||||
// Default timeout matches DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS from probe.ts
|
||||
const timeoutMs = opts?.timeoutMs ?? 10_000;
|
||||
|
||||
const response = new Promise<T>((resolve, reject) => {
|
||||
|
||||
@@ -45,7 +45,7 @@ import { resolveAgentRoute } from "../../routing/resolve-route.js";
|
||||
import { truncateUtf16Safe } from "../../utils.js";
|
||||
import { resolveIMessageAccount } from "../accounts.js";
|
||||
import { createIMessageRpcClient } from "../client.js";
|
||||
import { probeIMessage } from "../probe.js";
|
||||
import { DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS, probeIMessage } from "../probe.js";
|
||||
import { sendMessageIMessage } from "../send.js";
|
||||
import {
|
||||
formatIMessageChatTarget,
|
||||
@@ -139,6 +139,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
|
||||
const mediaMaxBytes = (opts.mediaMaxMb ?? imessageCfg.mediaMaxMb ?? 16) * 1024 * 1024;
|
||||
const cliPath = opts.cliPath ?? imessageCfg.cliPath ?? "imsg";
|
||||
const dbPath = opts.dbPath ?? imessageCfg.dbPath;
|
||||
const probeTimeoutMs = imessageCfg.probeTimeoutMs ?? DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS;
|
||||
|
||||
// Resolve remoteHost: explicit config, or auto-detect from SSH wrapper script
|
||||
let remoteHost = imessageCfg.remoteHost;
|
||||
@@ -618,7 +619,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
|
||||
abortSignal: opts.abortSignal,
|
||||
runtime,
|
||||
check: async () => {
|
||||
const probe = await probeIMessage(2000, { cliPath, dbPath, runtime });
|
||||
const probe = await probeIMessage(probeTimeoutMs, { cliPath, dbPath, runtime });
|
||||
if (probe.ok) {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import { loadConfig } from "../config/config.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { createIMessageRpcClient } from "./client.js";
|
||||
|
||||
/** Default timeout for iMessage probe operations (10 seconds). */
|
||||
export const DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS = 10_000;
|
||||
|
||||
export type IMessageProbe = {
|
||||
ok: boolean;
|
||||
error?: string | null;
|
||||
@@ -24,13 +27,13 @@ type RpcSupportResult = {
|
||||
|
||||
const rpcSupportCache = new Map<string, RpcSupportResult>();
|
||||
|
||||
async function probeRpcSupport(cliPath: string): Promise<RpcSupportResult> {
|
||||
async function probeRpcSupport(cliPath: string, timeoutMs: number): Promise<RpcSupportResult> {
|
||||
const cached = rpcSupportCache.get(cliPath);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
try {
|
||||
const result = await runCommandWithTimeout([cliPath, "rpc", "--help"], { timeoutMs: 2000 });
|
||||
const result = await runCommandWithTimeout([cliPath, "rpc", "--help"], { timeoutMs });
|
||||
const combined = `${result.stdout}\n${result.stderr}`.trim();
|
||||
const normalized = combined.toLowerCase();
|
||||
if (normalized.includes("unknown command") && normalized.includes("rpc")) {
|
||||
@@ -57,18 +60,24 @@ async function probeRpcSupport(cliPath: string): Promise<RpcSupportResult> {
|
||||
}
|
||||
|
||||
export async function probeIMessage(
|
||||
timeoutMs = 2000,
|
||||
timeoutMs = DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS,
|
||||
opts: IMessageProbeOptions = {},
|
||||
): Promise<IMessageProbe> {
|
||||
const cfg = opts.cliPath || opts.dbPath ? undefined : loadConfig();
|
||||
const cliPath = opts.cliPath?.trim() || cfg?.channels?.imessage?.cliPath?.trim() || "imsg";
|
||||
const dbPath = opts.dbPath?.trim() || cfg?.channels?.imessage?.dbPath?.trim();
|
||||
// Read probeTimeoutMs from config if not explicitly provided
|
||||
const effectiveTimeout =
|
||||
timeoutMs !== DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS
|
||||
? timeoutMs
|
||||
: cfg?.channels?.imessage?.probeTimeoutMs ?? DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS;
|
||||
|
||||
const detected = await detectBinary(cliPath);
|
||||
if (!detected) {
|
||||
return { ok: false, error: `imsg not found (${cliPath})` };
|
||||
}
|
||||
|
||||
const rpcSupport = await probeRpcSupport(cliPath);
|
||||
const rpcSupport = await probeRpcSupport(cliPath, effectiveTimeout);
|
||||
if (!rpcSupport.supported) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -83,7 +92,7 @@ export async function probeIMessage(
|
||||
runtime: opts.runtime,
|
||||
});
|
||||
try {
|
||||
await client.request("chats.list", { limit: 1 }, { timeoutMs });
|
||||
await client.request("chats.list", { limit: 1 }, { timeoutMs: effectiveTimeout });
|
||||
return { ok: true };
|
||||
} catch (err) {
|
||||
return { ok: false, error: String(err) };
|
||||
|
||||
Reference in New Issue
Block a user