mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
* fix: preserve complete skill artifacts * test: align artifact metadata expectations * fix: harden complete skill artifact handling * fix: close complete artifact review gaps * fix: preserve legacy skill file metadata hints * fix: close artifact presentation review gaps * fix(cli): preserve legacy skill file collector export * refactor: centralize artifact upload helpers * fix: preserve artifact scan and publish bounds * fix: scan complete published text artifacts * fix: harden artifact download presentation * test: avoid secret-like fixture text * refactor: preview artifacts by content * chore(deps): patch transitive audit advisories
68 lines
2.4 KiB
TypeScript
68 lines
2.4 KiB
TypeScript
import { normalizeContentType } from "clawhub-schema/textFiles";
|
|
import { getUserFacingConvexError } from "./convexError";
|
|
|
|
export async function uploadFile(uploadUrl: string, file: File) {
|
|
const contentType = normalizeContentType(file.type) || file.type || "application/octet-stream";
|
|
const response = await fetch(uploadUrl, {
|
|
method: "POST",
|
|
headers: { "Content-Type": contentType },
|
|
body: file,
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`Upload failed: ${await response.text()}`);
|
|
}
|
|
const payload = (await response.json()) as { storageId: string };
|
|
return payload.storageId;
|
|
}
|
|
|
|
export async function hashFile(file: File) {
|
|
const buffer =
|
|
typeof file.arrayBuffer === "function"
|
|
? await file.arrayBuffer()
|
|
: await new Response(file).arrayBuffer();
|
|
const hash = await crypto.subtle.digest("SHA-256", new Uint8Array(buffer));
|
|
const bytes = new Uint8Array(hash);
|
|
return Array.from(bytes)
|
|
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
}
|
|
|
|
export function formatBytes(bytes: number) {
|
|
if (!Number.isFinite(bytes)) return "0 B";
|
|
const units = ["B", "KB", "MB", "GB"];
|
|
let size = bytes;
|
|
let unit = 0;
|
|
while (size >= 1024 && unit < units.length - 1) {
|
|
size /= 1024;
|
|
unit += 1;
|
|
}
|
|
return `${size.toFixed(size < 10 && unit > 0 ? 1 : 0)} ${units[unit]}`;
|
|
}
|
|
|
|
export function formatPublishError(error: unknown) {
|
|
return getUserFacingConvexError(error, "Publish failed. Please try again.");
|
|
}
|
|
|
|
export async function readText(blob: Blob) {
|
|
if (typeof (blob as Blob & { text?: unknown }).text === "function") {
|
|
return (blob as Blob & { text: () => Promise<string> }).text();
|
|
}
|
|
if (typeof (blob as Blob & { arrayBuffer?: unknown }).arrayBuffer === "function") {
|
|
const buffer = await (blob as Blob & { arrayBuffer: () => Promise<ArrayBuffer> }).arrayBuffer();
|
|
return new TextDecoder().decode(new Uint8Array(buffer));
|
|
}
|
|
if (typeof FileReader !== "undefined" && blob instanceof Blob) {
|
|
return new Promise<string>((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.addEventListener("error", () => {
|
|
reject(reader.error ?? new Error("Could not read blob."));
|
|
});
|
|
reader.addEventListener("load", () => {
|
|
resolve(typeof reader.result === "string" ? reader.result : "");
|
|
});
|
|
reader.readAsText(blob);
|
|
});
|
|
}
|
|
return new Response(blob as BodyInit).text();
|
|
}
|