UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

156 lines (155 loc) 5.77 kB
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js"; import { n as resolveOpenClawPackageRootSync } from "./openclaw-root-CNp1Ofdk.js"; import { n as resolveGitHeadPath } from "./git-root-B5CYl5JZ.js"; import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; import fs from "node:fs"; import path from "node:path"; //#region src/infra/git-commit.ts const formatCommit = (value) => { if (!value) return null; const trimmed = value.trim(); if (!trimmed) return null; const match = trimmed.match(/[0-9a-fA-F]{7,40}/); if (!match) return null; return normalizeLowercaseStringOrEmpty(match[0].slice(0, 7)); }; const cachedGitCommitBySearchDir = /* @__PURE__ */ new Map(); function isMissingPathError(error) { if (!(error instanceof Error)) return false; const code = error.code; return code === "ENOENT" || code === "ENOTDIR"; } const resolveCommitSearchDir = (options) => { if (options.cwd) return path.resolve(options.cwd); if (options.moduleUrl) try { return path.dirname(fileURLToPath(options.moduleUrl)); } catch {} return process.cwd(); }; /** Read at most `limit` bytes from a file to avoid unbounded reads. */ const safeReadFilePrefix = (filePath, limit = 256) => { const fd = fs.openSync(filePath, "r"); try { const buf = Buffer.alloc(limit); const bytesRead = fs.readSync(fd, buf, 0, limit, 0); return buf.subarray(0, bytesRead).toString("utf-8"); } finally { fs.closeSync(fd); } }; const cacheGitCommit = (searchDir, commit) => { cachedGitCommitBySearchDir.set(searchDir, commit); return commit; }; const clearCachedGitCommits = () => { cachedGitCommitBySearchDir.clear(); }; const resolveGitLookupDepth = (searchDir, packageRoot) => { if (!packageRoot) return; const relative = path.relative(packageRoot, searchDir); if (relative.startsWith("..") || path.isAbsolute(relative)) return; return (relative ? relative.split(path.sep).filter(Boolean).length : 0) + 1; }; const readCommitFromGit = (searchDir, packageRoot) => { const headPath = resolveGitHeadPath(searchDir, { maxDepth: resolveGitLookupDepth(searchDir, packageRoot) }); if (!headPath) return; const head = fs.readFileSync(headPath, "utf-8").trim(); if (!head) return null; if (head.startsWith("ref:")) { const ref = head.replace(/^ref:\s*/i, "").trim(); const refsBase = resolveGitRefsBase(headPath); const refPath = resolveRefPath(refsBase, ref); if (!refPath) return null; try { return formatCommit(safeReadFilePrefix(refPath).trim()); } catch (error) { if (!isMissingPathError(error)) throw error; } return readCommitFromPackedRefs(refsBase, ref); } return formatCommit(head); }; const resolveGitRefsBase = (headPath) => { const gitDir = path.dirname(headPath); try { const commonDir = safeReadFilePrefix(path.join(gitDir, "commondir")).trim(); if (commonDir) return path.resolve(gitDir, commonDir); } catch (error) { if (!isMissingPathError(error)) throw error; } return gitDir; }; const readCommitFromPackedRefs = (refsBase, ref) => { 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 [commit, packedRef] = line.trim().split(/\s+/, 2); if (packedRef === ref) return formatCommit(commit); } return null; } catch (error) { if (!isMissingPathError(error)) throw error; return null; } }; /** Safely resolve a git ref path, rejecting traversal attacks from a crafted HEAD file. */ const 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; }; const readCommitFromPackageJson = () => { try { const pkg = createRequire(import.meta.url)("../../package.json"); return formatCommit(pkg.gitHead ?? pkg.githead ?? null); } catch { return null; } }; const readCommitFromBuildInfo = () => { try { const require = createRequire(import.meta.url); for (const candidate of ["../build-info.json", "./build-info.json"]) try { const formatted = formatCommit(require(candidate).commit ?? null); if (formatted) return formatted; } catch {} return null; } catch { return null; } }; const resolveCommitHash = (options = {}) => { const env = options.env ?? process.env; const readers = options.readers ?? {}; const readGitCommit = readers.readGitCommit ?? readCommitFromGit; const normalized = formatCommit(env.GIT_COMMIT?.trim() || env.GIT_SHA?.trim()); if (normalized) return normalized; const searchDir = resolveCommitSearchDir(options); if (cachedGitCommitBySearchDir.has(searchDir)) return cachedGitCommitBySearchDir.get(searchDir) ?? null; const packageRoot = resolveOpenClawPackageRootSync({ cwd: options.cwd, moduleUrl: options.moduleUrl }); try { const gitCommit = readGitCommit(searchDir, packageRoot); if (gitCommit !== void 0) return cacheGitCommit(searchDir, gitCommit); } catch {} const buildInfoCommit = readers.readBuildInfoCommit?.() ?? readCommitFromBuildInfo(); if (buildInfoCommit) return cacheGitCommit(searchDir, buildInfoCommit); const pkgCommit = readers.readPackageJsonCommit?.() ?? readCommitFromPackageJson(); if (pkgCommit) return cacheGitCommit(searchDir, pkgCommit); try { return cacheGitCommit(searchDir, readGitCommit(searchDir, packageRoot) ?? null); } catch { return cacheGitCommit(searchDir, null); } }; const testing = { clearCachedGitCommits }; //#endregion export { testing as n, resolveCommitHash as t };