UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

239 lines (238 loc) • 9.24 kB
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { r as resolveOpenClawPackageRootSync } from "./openclaw-root-CBoxhfke.mjs"; import { r as asNullableRecord } from "./record-coerce-TIsquoVR.mjs"; import { r as isMissingPathError } from "./errno-FR4CRyya.mjs"; import "./errors-O33D01XW.mjs"; import { t as LruCache } from "./lru-cache-C3Xgvc4Q.mjs"; import { createRequire } from "node:module"; import fs from "node:fs"; import { fileURLToPath } from "node:url"; 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 //#region src/infra/git-commit.ts const formatCommit = (value) => { const match = value?.match(/[0-9a-fA-F]{7,40}/); return match ? normalizeLowercaseStringOrEmpty(match[0].slice(0, 7)) : null; }; function gitCommitPrefixesMatch(left, right) { const normalizedLeft = normalizeLowercaseStringOrEmpty(left); const normalizedRight = normalizeLowercaseStringOrEmpty(right); return normalizedLeft.length >= 7 && normalizedRight.length >= 7 && (normalizedLeft.startsWith(normalizedRight) || normalizedRight.startsWith(normalizedLeft)); } const cachedGitCommitBySearchDir = new LruCache(256); 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(); }; const cacheGitCommit = (searchDir, commit) => { cachedGitCommitBySearchDir.set(searchDir, commit); return commit; }; 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 head = readGitHead(searchDir, { maxDepth: resolveGitLookupDepth(searchDir, packageRoot) }); return head === void 0 ? void 0 : formatCommit(head.value); }; const readCommitFromPackageJson = () => { try { const pkg = createRequire(import.meta.url)("../../package.json"); return formatCommit(pkg.gitHead ?? pkg.githead ?? null); } catch { return null; } }; const readCommitProbe = (moduleUrl, candidates, field) => { try { for (const candidate of candidates) { const filePath = fileURLToPath(new URL(candidate, moduleUrl)); let raw; try { raw = readGitMetadataPrefix(filePath, 1024); } catch (error) { if (isMissingPathError(error)) continue; return null; } try { const value = asNullableRecord(JSON.parse(raw))?.[field]; return typeof value === "string" ? formatCommit(value) : null; } catch { return null; } } } catch {} }; const readCommitFromBuildInfo = (moduleUrl = import.meta.url) => { return readCommitProbe(moduleUrl, ["../build-info.json", "./build-info.json"], "commit") ?? null; }; const readLoadedCommit = (moduleUrl) => { const buildStamp = readCommitProbe(moduleUrl, ["../.buildstamp", "./.buildstamp"], "head"); const runtimeStamp = readCommitProbe(moduleUrl, ["../.runtime-postbuildstamp", "./.runtime-postbuildstamp"], "head"); if (buildStamp !== void 0 || runtimeStamp !== void 0) { if (buildStamp || runtimeStamp) return buildStamp && buildStamp === runtimeStamp ? buildStamp : null; return readCommitFromBuildInfo(moduleUrl); } return readCommitProbe(moduleUrl, ["../build-info.json", "./build-info.json"], "commit"); }; const resolveCommitHash = (options = {}) => { const env = options.env ?? process.env; const readers = options.readers ?? {}; const readGitCommit = readers.readGitCommit ?? readCommitFromGit; const envCommit = env.GIT_COMMIT?.trim() || env.GIT_SHA?.trim(); const normalized = formatCommit(envCommit); if (normalized) return normalized; const searchDir = resolveCommitSearchDir(options); const cached = cachedGitCommitBySearchDir.get(searchDir); if (cached !== void 0) return cached; 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); } }; /** Resolve the commit that produced the loaded artifact, not the checkout's current revision. */ function resolveLoadedCommitHash(options = {}) { const moduleUrl = options.moduleUrl ?? import.meta.url; const loaded = readLoadedCommit(moduleUrl); return loaded === void 0 ? resolveCommitHash({ moduleUrl, ...options.env ? { env: options.env } : {} }) : loaded; } //#endregion export { readGitHead as a, resolveGitRefsBase as c, findGitRoot as i, resolveCommitHash as n, readGitMetadataPrefix as o, resolveLoadedCommitHash as r, readGitRefs as s, gitCommitPrefixesMatch as t };