






















@@ -3,6 +3,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises";
33import { extname, join } from "node:path";
44import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
55import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
6+import { appendIMessageCliStderrTail, appendIMessageCliStdout } from "./cli-output.js";
67import { createIMessageRpcClient } from "./client.js";
78import { extractMarkdownFormatRuns } from "./markdown-format.js";
89import { resolveIMessageMessageId as resolveIMessageMessageIdImpl } from "./monitor-reply-cache.js";
@@ -182,6 +183,31 @@ async function runIMessageCliJson(
182183let stdout = "";
183184let stderr = "";
184185let killEscalation: ReturnType<typeof setTimeout> | null = null;
186+let settled = false;
187+const clearTimers = (options: { keepKillEscalation?: boolean } = {}): void => {
188+if (timer) {
189+clearTimeout(timer);
190+}
191+if (killEscalation && !options.keepKillEscalation) {
192+clearTimeout(killEscalation);
193+}
194+};
195+const fail = (error: Error, options: { keepKillEscalation?: boolean } = {}): void => {
196+if (settled) {
197+return;
198+}
199+settled = true;
200+clearTimers(options);
201+reject(error);
202+};
203+const succeed = (value: Record<string, unknown>): void => {
204+if (settled) {
205+return;
206+}
207+settled = true;
208+clearTimers();
209+resolve(value);
210+};
185211const timer =
186212options.timeoutMs && options.timeoutMs > 0
187213 ? setTimeout(() => {
@@ -196,32 +222,43 @@ async function runIMessageCliJson(
196222// best-effort
197223}
198224}, 2000);
199-reject(new Error(`iMessage action timed out after ${options.timeoutMs}ms`));
225+fail(new Error(`iMessage action timed out after ${options.timeoutMs}ms`), {
226+keepKillEscalation: true,
227+});
200228}, options.timeoutMs)
201229 : null;
202230child.stdout.setEncoding("utf8");
203231child.stderr.setEncoding("utf8");
204232child.stdout.on("data", (chunk) => {
205-stdout += chunk;
233+if (settled) {
234+return;
235+}
236+const appended = appendIMessageCliStdout(stdout, chunk);
237+if (!appended.ok) {
238+try {
239+child.kill("SIGKILL");
240+} catch {
241+// best-effort
242+}
243+fail(new Error(appended.message));
244+return;
245+}
246+stdout = appended.value;
206247});
207248child.stderr.on("data", (chunk) => {
208-stderr += chunk;
249+stderr = appendIMessageCliStderrTail(stderr, chunk);
209250});
210251child.on("error", (error) => {
211-if (timer) {
212-clearTimeout(timer);
213-}
214-if (killEscalation) {
215-clearTimeout(killEscalation);
252+if (settled) {
253+clearTimers();
254+return;
216255}
217-reject(error);
256+fail(error);
218257});
219258child.on("close", (code) => {
220-if (timer) {
221-clearTimeout(timer);
222-}
223-if (killEscalation) {
224-clearTimeout(killEscalation);
259+if (settled) {
260+clearTimers();
261+return;
225262}
226263const lines = normalizeStringEntries(stdout.split(/\r?\n/));
227264const last = lines.at(-1);
@@ -242,22 +279,22 @@ async function runIMessageCliJson(
242279stderr.trim() ||
243280stdout.trim() ||
244281`imsg exited with code ${code}`;
245-reject(new Error(detail));
282+fail(new Error(detail));
246283return;
247284}
248285if (!parsed) {
249-reject(new Error(`imsg returned non-JSON output: ${stdout.trim() || stderr.trim()}`));
286+fail(new Error(`imsg returned non-JSON output: ${stdout.trim() || stderr.trim()}`));
250287return;
251288}
252289if (parsed.success === false) {
253290const error =
254291typeof parsed.error === "string" && parsed.error.trim()
255292 ? parsed.error.trim()
256293 : "iMessage action failed";
257-reject(new Error(error));
294+fail(new Error(error));
258295return;
259296}
260-resolve(parsed);
297+succeed(parsed);
261298});
262299});
263300}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。