feat: stream files as binary frames over WebSocket (#659)
Replace base64-in-JSON file transfers with binary WebSocket frames to
unblock the JS thread when assistant markdown messages contain images.
Receive cost (JSON.parse over multi-MB base64) and persist cost (base64
re-encode to disk) both removed.
- Split daemon-client `exploreFileSystem` into `listDirectory` + `readFile`;
`readFile` returns `{ bytes, mime, size, modifiedAt }`. Encoding stays
private to the seam.
- New `packages/server/src/shared/binary-frames/` module hosts terminal
frames (moved from `terminal-stream-protocol.ts`) and new file-transfer
opcodes (`FileBegin` / `FileChunk` / `FileEnd`) with request-id correlation.
- Add optional `acceptBinary` to `FileExplorerRequestSchema`. New daemons
emit binary frames when set; legacy JSON path remains for old clients.
Compat fallback lives only inside `readFile`.
- New `persistAttachmentFromBytes` writes bytes directly: native via
`expo-file-system` `File.write(Uint8Array)`, web via Blob -> IndexedDB,
desktop via new `write_attachment_bytes` IPC. `persistAttachmentFromBase64`
removed.
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
deleteDesktopAttachmentFile,
|
||||
garbageCollectDesktopAttachmentFiles,
|
||||
writeDesktopAttachmentBase64,
|
||||
writeDesktopAttachmentBytes,
|
||||
} from "@/desktop/attachments/desktop-file-commands";
|
||||
import {
|
||||
readDesktopFileBase64,
|
||||
@@ -132,6 +133,32 @@ async function saveDesktopAttachmentFromBase64(input: {
|
||||
});
|
||||
}
|
||||
|
||||
async function saveDesktopAttachmentFromBytes(input: {
|
||||
id: string;
|
||||
bytes: Uint8Array;
|
||||
mimeType: string;
|
||||
fileName?: string | null;
|
||||
}): Promise<AttachmentMetadata> {
|
||||
const extension = extensionForAttachment({
|
||||
fileName: input.fileName,
|
||||
mimeType: input.mimeType,
|
||||
});
|
||||
|
||||
const result = await writeDesktopAttachmentBytes({
|
||||
attachmentId: input.id,
|
||||
bytes: input.bytes,
|
||||
extension,
|
||||
});
|
||||
|
||||
return toDesktopMetadata({
|
||||
id: input.id,
|
||||
mimeType: input.mimeType,
|
||||
storagePath: result.path,
|
||||
byteSize: result.byteSize,
|
||||
fileName: input.fileName,
|
||||
});
|
||||
}
|
||||
|
||||
function assertDesktopAttachment(attachment: AttachmentMetadata): void {
|
||||
if (attachment.storageType !== "desktop-file") {
|
||||
throw new Error(`Unsupported desktop attachment storage type '${attachment.storageType}'.`);
|
||||
@@ -166,11 +193,11 @@ export function createDesktopAttachmentStore(): AttachmentStore {
|
||||
});
|
||||
}
|
||||
|
||||
if (input.source.kind === "base64") {
|
||||
if (input.source.kind === "bytes") {
|
||||
const mimeType = normalizeMimeType(input.mimeType);
|
||||
return await saveDesktopAttachmentFromBase64({
|
||||
return await saveDesktopAttachmentFromBytes({
|
||||
id,
|
||||
base64: input.source.base64,
|
||||
bytes: input.source.bytes,
|
||||
mimeType,
|
||||
fileName,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user