openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
244 lines (243 loc) • 7.26 kB
JavaScript
import { i as formatErrorMessage } from "./errors-BXgSefBE.js";
import { l as normalizeStringEntries } from "./string-normalization-WNUDCpXX.js";
import { r as runCommandWithTimeout } from "./exec-DubsSJS2.js";
import { a as prepareArchiveDestinationDir, i as mergeExtractedTreeIntoDestination, o as withStagedArchiveDestination, r as createTarEntryPreflightChecker, t as extractArchive$1 } from "./archive-Dcpo6Wva.js";
import "./archive-CBe_wA_B.js";
import "./config-CEJFxFUy.js";
import { n as hasBinary } from "./config-eval-CAHzuwOy.js";
import fs from "node:fs";
import { createHash } from "node:crypto";
//#region src/skills/lifecycle/install-tar-verbose.ts
const TAR_VERBOSE_MONTHS = new Set([
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
]);
const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
function mapTarVerboseTypeChar(typeChar) {
switch (typeChar) {
case "l": return "SymbolicLink";
case "h": return "Link";
case "b": return "BlockDevice";
case "c": return "CharacterDevice";
case "p": return "FIFO";
case "s": return "Socket";
case "d": return "Directory";
default: return "File";
}
}
function parseTarVerboseSize(line) {
const tokens = line.trim().split(/\s+/).filter(Boolean);
if (tokens.length < 6) throw new Error(`unable to parse tar verbose metadata: ${line}`);
let dateIndex = tokens.findIndex((token) => TAR_VERBOSE_MONTHS.has(token));
if (dateIndex > 0) return parseTarSizeToken(tokens[dateIndex - 1] ?? "", line);
dateIndex = tokens.findIndex((token) => ISO_DATE_PATTERN.test(token));
if (dateIndex > 0) return parseTarSizeToken(tokens[dateIndex - 1] ?? "", line);
throw new Error(`unable to parse tar verbose metadata: ${line}`);
}
function parseTarSizeToken(raw, line) {
if (!/^\d+$/.test(raw)) throw new Error(`unable to parse tar entry size: ${line}`);
const size = Number(raw);
if (!Number.isSafeInteger(size)) throw new Error(`unable to parse tar entry size: ${line}`);
return size;
}
/** Parses tar verbose metadata into type and byte size entries. */
function parseTarVerboseMetadata(stdout) {
return normalizeStringEntries(stdout.split("\n")).map((line) => {
const typeChar = line[0] ?? "";
if (!typeChar) throw new Error("unable to parse tar entry type");
return {
type: mapTarVerboseTypeChar(typeChar),
size: parseTarVerboseSize(line)
};
});
}
//#endregion
//#region src/skills/lifecycle/install-extract.ts
async function hashFileSha256(filePath) {
const hash = createHash("sha256");
const stream = fs.createReadStream(filePath);
return await new Promise((resolve, reject) => {
stream.on("data", (chunk) => {
hash.update(chunk);
});
stream.on("error", reject);
stream.on("end", () => {
resolve(hash.digest("hex"));
});
});
}
function commandFailureResult(result, fallbackStderr) {
return {
stdout: result.stdout,
stderr: result.stderr || fallbackStderr,
code: result.code
};
}
function buildTarExtractArgv(params) {
const argv = [
"tar",
"xf",
params.archivePath,
"-C",
params.targetDir
];
if (params.stripComponents > 0) argv.push("--strip-components", String(params.stripComponents));
return argv;
}
async function readTarPreflight(params) {
const listResult = await runCommandWithTimeout([
"tar",
"tf",
params.archivePath
], { timeoutMs: params.timeoutMs });
if (listResult.code !== 0) return commandFailureResult(listResult, "tar list failed");
const entries = normalizeStringEntries(listResult.stdout.split("\n"));
const verboseResult = await runCommandWithTimeout([
"tar",
"tvf",
params.archivePath
], { timeoutMs: params.timeoutMs });
if (verboseResult.code !== 0) return commandFailureResult(verboseResult, "tar verbose list failed");
const metadata = parseTarVerboseMetadata(verboseResult.stdout);
if (metadata.length !== entries.length) return {
stdout: verboseResult.stdout,
stderr: `tar verbose/list entry count mismatch (${metadata.length} vs ${entries.length})`,
code: 1
};
return {
entries,
metadata
};
}
function isArchiveExtractFailure(value) {
return "code" in value;
}
async function verifyArchiveHashStable(params) {
if (await hashFileSha256(params.archivePath) === params.expectedHash) return null;
return {
stdout: "",
stderr: "tar archive changed during safety preflight; refusing to extract",
code: 1
};
}
async function extractTarBz2WithStaging(params) {
return await withStagedArchiveDestination({
destinationRealDir: params.destinationRealDir,
run: async (stagingDir) => {
const extractResult = await runCommandWithTimeout(buildTarExtractArgv({
archivePath: params.archivePath,
targetDir: stagingDir,
stripComponents: params.stripComponents
}), { timeoutMs: params.timeoutMs });
if (extractResult.code !== 0) return extractResult;
await mergeExtractedTreeIntoDestination({
sourceDir: stagingDir,
destinationDir: params.destinationRealDir,
destinationRealDir: params.destinationRealDir
});
return extractResult;
}
});
}
async function extractArchive(params) {
const { archivePath, archiveType, targetDir, stripComponents, timeoutMs } = params;
const strip = typeof stripComponents === "number" && Number.isFinite(stripComponents) ? Math.max(0, Math.floor(stripComponents)) : 0;
try {
if (archiveType === "zip") {
await extractArchive$1({
archivePath,
destDir: targetDir,
timeoutMs,
kind: "zip",
stripComponents: strip
});
return {
stdout: "",
stderr: "",
code: 0
};
}
if (archiveType === "tar.gz") {
await extractArchive$1({
archivePath,
destDir: targetDir,
timeoutMs,
kind: "tar",
stripComponents: strip,
tarGzip: true
});
return {
stdout: "",
stderr: "",
code: 0
};
}
if (archiveType === "tar.bz2") {
if (!hasBinary("tar")) return {
stdout: "",
stderr: "tar not found on PATH",
code: null
};
const destinationRealDir = await prepareArchiveDestinationDir(targetDir);
const preflightHash = await hashFileSha256(archivePath);
const preflight = await readTarPreflight({
archivePath,
timeoutMs
});
if (isArchiveExtractFailure(preflight)) return preflight;
const checkTarEntrySafety = createTarEntryPreflightChecker({
rootDir: destinationRealDir,
stripComponents: strip,
escapeLabel: "targetDir"
});
for (let i = 0; i < preflight.entries.length; i += 1) {
const entryPath = preflight.entries[i];
const entryMeta = preflight.metadata[i];
if (!entryPath || !entryMeta) return {
stdout: "",
stderr: "tar metadata parse failure",
code: 1
};
checkTarEntrySafety({
path: entryPath,
type: entryMeta.type,
size: entryMeta.size
});
}
const hashFailure = await verifyArchiveHashStable({
archivePath,
expectedHash: preflightHash
});
if (hashFailure) return hashFailure;
return await extractTarBz2WithStaging({
archivePath,
destinationRealDir,
stripComponents: strip,
timeoutMs
});
}
return {
stdout: "",
stderr: `unsupported archive type: ${archiveType}`,
code: null
};
} catch (err) {
return {
stdout: "",
stderr: formatErrorMessage(err),
code: 1
};
}
}
//#endregion
export { extractArchive };