openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
239 lines (238 loc) • 9.17 kB
JavaScript
import { n as resolveOpenClawPackageRootSync } from "./openclaw-root-CfYY-fyD.js";
import { r as asNullableRecord } from "./record-coerce-DItp3I4t.js";
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { r as isMissingPathError } from "./errno-CkbDOfLk.js";
import { t as pruneMapToMaxSize } from "./map-size-CNcWiFKu.js";
import "./errors-Db3Ymjlb.js";
import { n as readFileWindowFullySync } from "./file-read-DtMn74uz.js";
import { createRequire } from "node:module";
import fs from "node:fs";
import { fileURLToPath } from "node:url";
import path from "node: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 gitPath = path.join(repoRoot, ".git");
try {
const stat = fs.statSync(gitPath);
return stat.isDirectory() || stat.isFile();
} catch {
return 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;
});
}
//#endregion
//#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));
};
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 = /* @__PURE__ */ new Map();
const GIT_COMMIT_CACHE_LIMIT = 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();
};
/** 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 = readFileWindowFullySync(fd, buf, 0);
return buf.subarray(0, bytesRead).toString("utf-8");
} finally {
fs.closeSync(fd);
}
};
const cacheGitCommit = (searchDir, commit) => {
cachedGitCommitBySearchDir.set(searchDir, commit);
pruneMapToMaxSize(cachedGitCommitBySearchDir, GIT_COMMIT_CACHE_LIMIT);
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 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 {
const refHash = safeReadFilePrefix(refPath).trim();
return formatCommit(refHash);
} 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 = () => {
if (typeof WORKER_DEPLOY_BUILD === "boolean" && WORKER_DEPLOY_BUILD) return null;
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 = safeReadFilePrefix(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);
if (cachedGitCommitBySearchDir.has(searchDir)) {
const cached = cachedGitCommitBySearchDir.get(searchDir) ?? null;
cachedGitCommitBySearchDir.delete(searchDir);
cachedGitCommitBySearchDir.set(searchDir, cached);
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 { findGitRoot as i, resolveCommitHash as n, resolveLoadedCommitHash as r, gitCommitPrefixesMatch as t };