fix(cli): fall back to sips for HEIC infer inputs

This commit is contained in:
Peter Steinberger
2026-05-07 14:28:27 +01:00
parent 6ce1c98b61
commit a85261932e
3 changed files with 48 additions and 2 deletions

View File

@@ -179,6 +179,7 @@ Docs: https://docs.openclaw.ai
- Anthropic: reject uppercase provider-prefixed forward-compat model ids locally instead of sending malformed dynamic ids upstream. Fixes #73715.
- OpenAI/embeddings: pass configured output dimensionality through single and batched embedding requests so memory embedding indexes can request smaller vectors. Fixes #55126.
- CLI/infer: normalize HEIC/HEIF image files to JPEG before model-run requests, avoiding providers that reject Apple image container formats. Fixes #50081.
- CLI/infer: fall back to macOS `sips` when optional image tooling cannot decode HEIC/HEIF input files before model-run requests. Refs #50081.
- OpenRouter: keep the default `openrouter/auto` model ref canonical while preventing TUI and Control UI catalog pickers from displaying or submitting `openrouter/openrouter/auto`. Fixes #62655.
- Status/Claude CLI: show `oauth (claude-cli)` for working Claude CLI OAuth runtime sessions instead of `unknown` when no local auth profile exists. Fixes #78632. Thanks @gorkem2020.
- Memory search: preserve keyword-only hybrid FTS matches when vector scoring is unavailable or below the configured minimum score, so exact lexical hits are not dropped by weighted min-score filtering.

View File

@@ -1,7 +1,19 @@
import { spawnSync } from "node:child_process";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { getImageMetadata, MAX_IMAGE_INPUT_PIXELS, resizeToJpeg } from "./image-ops.js";
import {
convertHeicToJpeg,
getImageMetadata,
MAX_IMAGE_INPUT_PIXELS,
resizeToJpeg,
} from "./image-ops.js";
import { createPngBufferWithDimensions } from "./test-helpers.js";
const PNG_1X1_BASE64 =
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADUlEQVR4nGP8z8BQDwAFgwJ/lH3vWQAAAABJRU5ErkJggg==";
describe("image input pixel guard", () => {
const oversizedPng = createPngBufferWithDimensions({ width: 8_000, height: 4_000 });
const overflowedPng = createPngBufferWithDimensions({
@@ -53,4 +65,30 @@ describe("image input pixel guard", () => {
}
}
});
const itIfMac = process.platform === "darwin" ? it : it.skip;
itIfMac("converts macOS-generated HEIC images to JPEG", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-heic-convert-"));
try {
const pngPath = path.join(tempDir, "input.png");
const heicPath = path.join(tempDir, "input.heic");
await fs.writeFile(pngPath, Buffer.from(PNG_1X1_BASE64, "base64"));
const result = spawnSync(
"/usr/bin/sips",
["-s", "format", "heic", pngPath, "--out", heicPath],
{
encoding: "utf8",
},
);
expect(result.status, result.stderr || result.stdout).toBe(0);
const jpeg = await convertHeicToJpeg(await fs.readFile(heicPath));
expect(jpeg[0]).toBe(0xff);
expect(jpeg[1]).toBe(0xd8);
} finally {
await fs.rm(tempDir, { force: true, recursive: true });
}
});
});

View File

@@ -572,7 +572,14 @@ export async function convertHeicToJpeg(buffer: Buffer): Promise<Buffer> {
return await sipsConvertToJpeg(buffer);
}
const ops = await loadMediaAttachmentImageOps();
return await ops.convertHeicToJpeg(buffer);
try {
return await ops.convertHeicToJpeg(buffer);
} catch (error) {
if (process.platform !== "darwin") {
throw error;
}
return await sipsConvertToJpeg(buffer);
}
}
/**