mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-16 21:50:45 +00:00
Merged via squash.
Prepared head SHA: ee72657bc8
Co-authored-by: omarshahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: omarshahine <10343873+omarshahine@users.noreply.github.com>
Reviewed-by: @omarshahine
58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import type { SsrFPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
import { blueBubblesFetchWithTimeout } from "./types.js";
|
|
|
|
export function concatUint8Arrays(parts: Uint8Array[]): Uint8Array {
|
|
const totalLength = parts.reduce((acc, part) => acc + part.length, 0);
|
|
const body = new Uint8Array(totalLength);
|
|
let offset = 0;
|
|
for (const part of parts) {
|
|
body.set(part, offset);
|
|
offset += part.length;
|
|
}
|
|
return body;
|
|
}
|
|
|
|
export async function postMultipartFormData(params: {
|
|
url: string;
|
|
boundary: string;
|
|
parts: Uint8Array[];
|
|
timeoutMs: number;
|
|
ssrfPolicy?: SsrFPolicy;
|
|
/**
|
|
* Extra headers to merge with the multipart Content-Type. Used to forward
|
|
* auth-decorated headers from `BlueBubblesClient` (e.g. `X-BB-Password`
|
|
* under header-auth mode). Per-request Content-Type wins over callers so
|
|
* the multipart boundary is always authoritative. (Greptile #68234 P1)
|
|
*/
|
|
extraHeaders?: HeadersInit;
|
|
}): Promise<Response> {
|
|
const body = Buffer.from(concatUint8Arrays(params.parts));
|
|
const headers: Record<string, string> = {};
|
|
if (params.extraHeaders) {
|
|
new Headers(params.extraHeaders).forEach((value, key) => {
|
|
headers[key] = value;
|
|
});
|
|
}
|
|
// Per-request Content-Type wins over callers so the multipart boundary is
|
|
// always authoritative.
|
|
headers["Content-Type"] = `multipart/form-data; boundary=${params.boundary}`;
|
|
return await blueBubblesFetchWithTimeout(
|
|
params.url,
|
|
{
|
|
method: "POST",
|
|
headers,
|
|
body,
|
|
},
|
|
params.timeoutMs,
|
|
params.ssrfPolicy,
|
|
);
|
|
}
|
|
|
|
export async function assertMultipartActionOk(response: Response, action: string): Promise<void> {
|
|
if (response.ok) {
|
|
return;
|
|
}
|
|
const errorText = await response.text().catch(() => "");
|
|
throw new Error(`BlueBubbles ${action} failed (${response.status}): ${errorText || "unknown"}`);
|
|
}
|