fix: bound signal/imessage transport readiness waits

Co-authored-by: Szpadel <1857251+Szpadel@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-16 20:33:04 +00:00
parent 0cd24137e8
commit aaa310c047
9 changed files with 172 additions and 26 deletions

View File

@@ -54,6 +54,10 @@ vi.mock("./client.js", () => ({
}),
}));
vi.mock("./probe.js", () => ({
probeIMessage: vi.fn(async () => ({ ok: true })),
}));
const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
async function waitForSubscribe() {

View File

@@ -54,6 +54,10 @@ vi.mock("./client.js", () => ({
}),
}));
vi.mock("./probe.js", () => ({
probeIMessage: vi.fn(async () => ({ ok: true })),
}));
const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
async function waitForSubscribe() {

View File

@@ -30,6 +30,7 @@ import {
} from "../../config/group-policy.js";
import { resolveStorePath, updateLastRoute } from "../../config/sessions.js";
import { danger, logVerbose, shouldLogVerbose } from "../../globals.js";
import { waitForTransportReady } from "../../infra/transport-ready.js";
import { mediaKindFromMime } from "../../media/constants.js";
import { buildPairingReply } from "../../pairing/pairing-messages.js";
import {
@@ -40,6 +41,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 { sendMessageIMessage } from "../send.js";
import {
formatIMessageChatTarget,
@@ -76,6 +78,8 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
const dmPolicy = imessageCfg.dmPolicy ?? "pairing";
const includeAttachments = opts.includeAttachments ?? imessageCfg.includeAttachments ?? false;
const mediaMaxBytes = (opts.mediaMaxMb ?? imessageCfg.mediaMaxMb ?? 16) * 1024 * 1024;
const cliPath = opts.cliPath ?? imessageCfg.cliPath ?? "imsg";
const dbPath = opts.dbPath ?? imessageCfg.dbPath;
const inboundDebounceMs = resolveInboundDebounceMs({ cfg, channel: "imessage" });
const inboundDebouncer = createInboundDebouncer<{ message: IMessagePayload }>({
@@ -453,9 +457,26 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
await inboundDebouncer.enqueue({ message });
};
await waitForTransportReady({
label: "imsg rpc",
timeoutMs: 30_000,
logAfterMs: 10_000,
logIntervalMs: 10_000,
pollIntervalMs: 500,
abortSignal: opts.abortSignal,
runtime,
check: async () => {
const probe = await probeIMessage(2000, { cliPath, dbPath, runtime });
if (probe.ok) return { ok: true };
return { ok: false, error: probe.error ?? "unreachable" };
},
});
if (opts.abortSignal?.aborted) return;
const client = await createIMessageRpcClient({
cliPath: opts.cliPath ?? imessageCfg.cliPath,
dbPath: opts.dbPath ?? imessageCfg.dbPath,
cliPath,
dbPath,
runtime,
onNotification: (msg) => {
if (msg.method === "message") {

View File

@@ -1,5 +1,6 @@
import { detectBinary } from "../commands/onboard-helpers.js";
import { loadConfig } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import { createIMessageRpcClient } from "./client.js";
export type IMessageProbe = {
@@ -7,10 +8,19 @@ export type IMessageProbe = {
error?: string | null;
};
export async function probeIMessage(timeoutMs = 2000): Promise<IMessageProbe> {
const cfg = loadConfig();
const cliPath = cfg.channels?.imessage?.cliPath?.trim() || "imsg";
const dbPath = cfg.channels?.imessage?.dbPath?.trim();
export type IMessageProbeOptions = {
cliPath?: string;
dbPath?: string;
runtime?: RuntimeEnv;
};
export async function probeIMessage(
timeoutMs = 2000,
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();
const detected = await detectBinary(cliPath);
if (!detected) {
return { ok: false, error: `imsg not found (${cliPath})` };
@@ -19,6 +29,7 @@ export async function probeIMessage(timeoutMs = 2000): Promise<IMessageProbe> {
const client = await createIMessageRpcClient({
cliPath,
dbPath,
runtime: opts.runtime,
});
try {
await client.request("chats.list", { limit: 1 }, { timeoutMs });