Something went wrong. Try again.
presents omp as codex for use with chatgpt remote
Something went wrong. Try again.
4.2 kB · 101 lines
JavaScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102import { createHash } from "node:crypto";import fs from "node:fs/promises";
function align4(value) { return (value + 3) & ~3;}
function walkFiles(node, prefix = [], output = []) { for (const [name, entry] of Object.entries(node.files || {})) { const memberPath = [...prefix, name]; if (entry.files) walkFiles(entry, memberPath, output); else output.push({ entry, memberPath: memberPath.join("/") }); } return output;}
function memberIntegrity(content, blockSize = 4 * 1024 * 1024) { const blocks = []; for (let offset = 0; offset < content.length; offset += blockSize) { blocks.push(createHash("sha256").update(content.subarray(offset, offset + blockSize)).digest("hex")); } return { algorithm: "SHA256", hash: createHash("sha256").update(content).digest("hex"), blockSize, blocks, };}
export function parseAsar(archive) { if (!Buffer.isBuffer(archive) || archive.length < 16) throw new Error("invalid ASAR archive"); const jsonLength = archive.readUInt32LE(12); const headerEnd = 16 + jsonLength; if (headerEnd > archive.length) throw new Error("invalid ASAR header length"); const headerBytes = archive.subarray(16, headerEnd); const header = JSON.parse(headerBytes.toString("utf8")); const dataOffset = 8 + archive.readUInt32LE(4); return { dataOffset, header, headerBytes };}
export function readAsarMember(archive, memberPath) { const { dataOffset, header } = parseAsar(archive); const file = walkFiles(header).find((candidate) => candidate.memberPath === memberPath); if (!file || file.entry.unpacked) throw new Error(`packed ASAR member not found: ${memberPath}`); const offset = Number(file.entry.offset); return archive.subarray(dataOffset + offset, dataOffset + offset + file.entry.size);}
export function replaceAsarMember(archive, memberPath, replacement) { const { dataOffset, header } = parseAsar(archive); const files = walkFiles(header); const target = files.find((candidate) => candidate.memberPath === memberPath); if (!target || target.entry.unpacked) throw new Error(`packed ASAR member not found: ${memberPath}`);
const targetOffset = Number(target.entry.offset); const oldSize = target.entry.size; const delta = replacement.length - oldSize; target.entry.size = replacement.length; target.entry.integrity = memberIntegrity(replacement, target.entry.integrity?.blockSize);
for (const file of files) { if (file.entry.unpacked || file === target) continue; const offset = Number(file.entry.offset); if (offset > targetOffset) file.entry.offset = String(offset + delta); }
const headerBytes = Buffer.from(JSON.stringify(header)); const paddedStringLength = align4(headerBytes.length + 1); const newDataOffset = 16 + paddedStringLength; const prefix = Buffer.alloc(newDataOffset); prefix.writeUInt32LE(4, 0); prefix.writeUInt32LE(8 + paddedStringLength, 4); prefix.writeUInt32LE(4 + paddedStringLength, 8); prefix.writeUInt32LE(headerBytes.length, 12); headerBytes.copy(prefix, 16);
const oldStart = dataOffset + targetOffset; const oldEnd = oldStart + oldSize; const rebuilt = Buffer.concat([prefix, archive.subarray(dataOffset, oldStart), replacement, archive.subarray(oldEnd)]); return { archive: rebuilt, headerHash: createHash("sha256").update(headerBytes).digest("hex"), };}
export async function patchAsarMember(archivePath, memberPath, transform) { const archive = await fs.readFile(archivePath); const current = readAsarMember(archive, memberPath); const replacement = Buffer.from(await transform(current.toString("utf8"))); if (replacement.equals(current)) { const { headerBytes } = parseAsar(archive); return { changed: false, headerHash: createHash("sha256").update(headerBytes).digest("hex") }; }
const rebuilt = replaceAsarMember(archive, memberPath, replacement); const temporaryPath = `${archivePath}.omp-tmp-${process.pid}`; const stat = await fs.stat(archivePath); await fs.writeFile(temporaryPath, rebuilt.archive, { mode: stat.mode }); await fs.rename(temporaryPath, archivePath); return { changed: true, headerHash: rebuilt.headerHash };}