openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
123 lines (122 loc) • 4.28 kB
JavaScript
import { r as isMissingPathError } from "./errno-FR4CRyya.mjs";
import "./errors-BSdPMFao.mjs";
import fs from "node:fs";
import path from "node:path";
import { readFileWindowFullySync } from "@openclaw/fs-safe/advanced";
import { safeStatSync } from "@openclaw/fs-safe/path";
//#region src/infra/git-root.ts
function walkUpFrom(startDir, opts, resolveAtDir) {
let current = path.resolve(startDir);
for (let i = 0; opts.maxDepth === void 0 || i < opts.maxDepth; i += 1) {
const resolved = resolveAtDir(current);
if (resolved !== null && resolved !== void 0) return resolved;
const parent = path.dirname(current);
if (parent === current) break;
current = parent;
}
return null;
}
function hasGitMarker(repoRoot) {
const stat = safeStatSync(path.join(repoRoot, ".git"));
return stat ? stat.isDirectory() || stat.isFile() : false;
}
function findGitRoot(startDir, opts = {}) {
return walkUpFrom(startDir, opts, (repoRoot) => hasGitMarker(repoRoot) ? repoRoot : null);
}
function resolveGitDirFromMarker(repoRoot) {
const gitPath = path.join(repoRoot, ".git");
try {
const stat = fs.statSync(gitPath);
if (stat.isDirectory()) return gitPath;
if (!stat.isFile()) return null;
const match = fs.readFileSync(gitPath, "utf-8").match(/gitdir:\s*(.+)/i);
if (!match?.[1]) return null;
return path.resolve(repoRoot, match[1].trim());
} catch {
return null;
}
}
function resolveGitHeadPath(startDir, opts = {}) {
return walkUpFrom(startDir, opts, (repoRoot) => {
const gitDir = resolveGitDirFromMarker(repoRoot);
return gitDir ? path.join(gitDir, "HEAD") : null;
});
}
/** Read at most `limit` bytes from Git or build metadata. */
function readGitMetadataPrefix(filePath, limit = 256) {
const fd = fs.openSync(filePath, "r");
try {
const buf = Buffer.alloc(limit);
const bytesRead = readFileWindowFullySync(fd, buf, 0);
return buf.subarray(0, bytesRead).toString("utf-8");
} finally {
fs.closeSync(fd);
}
}
function readGitHead(startDir, opts = {}) {
const headPath = resolveGitHeadPath(startDir, opts);
if (!headPath) return;
const head = fs.readFileSync(headPath, "utf-8").trim();
if (!head.startsWith("ref:")) return {
headPath,
ref: null,
value: head || null
};
const ref = head.replace(/^ref:\s*/i, "").trim();
const refsBase = resolveGitRefsBase(headPath);
return {
headPath,
ref,
value: readGitRefs(refsBase, [ref]).get(ref) ?? null,
refsBase
};
}
function resolveGitRefsBase(headPath) {
const gitDir = path.dirname(headPath);
try {
const commonDir = readGitMetadataPrefix(path.join(gitDir, "commondir")).trim();
if (commonDir) return path.resolve(gitDir, commonDir);
} catch (error) {
if (!isMissingPathError(error)) throw error;
}
return gitDir;
}
/** Raw ref contents, sharing one packed inventory across the requested names. */
function readGitRefs(refsBase, refs) {
const values = new Map(refs.map((ref) => [ref, null]));
const missing = /* @__PURE__ */ new Set();
for (const ref of refs) {
const refPath = resolveRefPath(refsBase, ref);
if (!refPath) continue;
try {
values.set(ref, readGitMetadataPrefix(refPath).trim());
} catch (error) {
if (!isMissingPathError(error)) throw error;
missing.add(ref);
}
}
if (missing.size === 0) return values;
try {
const packedRefs = fs.readFileSync(path.join(refsBase, "packed-refs"), "utf-8");
for (const line of packedRefs.split("\n")) {
if (!line || line.startsWith("#") || line.startsWith("^")) continue;
const [value, packedRef] = line.trim().split(/\s+/, 2);
if (packedRef && missing.delete(packedRef)) values.set(packedRef, value ?? null);
}
} catch (error) {
if (!isMissingPathError(error)) throw error;
}
return values;
}
/** Safely resolve a Git ref path, rejecting traversal from a crafted HEAD file. */
function resolveRefPath(refsBase, ref) {
if (!ref.startsWith("refs/")) return null;
if (path.isAbsolute(ref)) return null;
if (ref.split(/[/]/).includes("..")) return null;
const resolved = path.resolve(refsBase, ref);
const rel = path.relative(refsBase, resolved);
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return null;
return resolved;
}
//#endregion
export { resolveGitRefsBase as a, readGitRefs as i, readGitHead as n, readGitMetadataPrefix as r, findGitRoot as t };