Files
OmniRoute/open-sse/utils/cursorAgentProtobuf/imageEncoding.ts
Diego Rodrigues de Sa e Souza d11b99f6cc cherry-pick(pr-9834): fix(cursor): SelectedImage blobIdWithData + JPEG soft-cap prep (#9840)
* fix(cursor): hydrate SelectedImage via blobIdWithData + JPEG soft-cap

Cursor vision expects SelectedImage.blob_id_with_data (field 9) backed by
the session blobStore, and large clipboard PNGs need JPEG soft-cap prep
rather than a hard 1 MiB reject before encode.

* docs(changelog): add fragment for Cursor SelectedImage blobIdWithData fix

* refactor(cursor): split image protobuf encoding

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: SB Yoon <44089734+yansigit@users.noreply.github.com>
2026-08-09 09:47:07 -03:00

81 lines
1.9 KiB
TypeScript

import crypto from "node:crypto";
import {
encodeBytes,
encodeMessage,
encodeString,
encodeUInt32Field,
} from "./wire.ts";
const SI_UUID = 2;
const SI_PATH = 3;
const SI_DIMENSION = 4;
const SI_MIME_TYPE = 7;
const SI_BLOB_ID_WITH_DATA = 9;
const SIBD_BLOB_ID = 1;
const SIBD_DATA = 2;
const DIM_WIDTH = 1;
const DIM_HEIGHT = 2;
export type EncodedImage = {
data: Buffer;
mimeType?: string;
width?: number;
height?: number;
uuid: string;
};
export function cursorImageAttachmentPath(uuid: string, mimeType?: string): string {
const normalized = (mimeType || "").toLowerCase();
const ext =
normalized === "image/jpeg" || normalized === "image/jpg"
? "jpg"
: normalized === "image/gif"
? "gif"
: normalized === "image/webp"
? "webp"
: "png";
return `attachment-${uuid}.${ext}`;
}
export function encodeSelectedImageBody(
img: EncodedImage,
blobStore?: Map<string, Buffer>
): Buffer {
const blobId = crypto.createHash("sha256").update(img.data).digest();
if (blobStore) {
blobStore.set(blobId.toString("hex"), img.data);
}
const parts: Buffer[] = [
encodeString(SI_UUID, img.uuid),
encodeString(SI_PATH, cursorImageAttachmentPath(img.uuid, img.mimeType)),
];
if (
typeof img.width === "number" &&
typeof img.height === "number" &&
Number.isFinite(img.width) &&
Number.isFinite(img.height) &&
img.width > 0 &&
img.height > 0
) {
parts.push(
encodeMessage(SI_DIMENSION, [
encodeUInt32Field(DIM_WIDTH, Math.floor(img.width)),
encodeUInt32Field(DIM_HEIGHT, Math.floor(img.height)),
])
);
}
if (img.mimeType) {
parts.push(encodeString(SI_MIME_TYPE, img.mimeType));
}
parts.push(
encodeMessage(SI_BLOB_ID_WITH_DATA, [
encodeBytes(SIBD_BLOB_ID, blobId),
encodeBytes(SIBD_DATA, img.data),
])
);
return Buffer.concat(parts);
}