UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

193 lines (192 loc) • 7.39 kB
import { t as mergeProcessEnv } from "./process-env-DlZFJzq6.mjs"; import { t as hasNodeErrorCode } from "./path-guards-CH7noY0Z.mjs"; import { c as gitNullConfigPath, d as requireGitCommandOutput, l as normalizeGitPathForFilesystem, n as createGitCommandError } from "./git-exec-DXEiUR5e.mjs"; import { c as requireGit, f as runGit, m as runGitBytes, p as runGitBuffered, r as gitEnvironment } from "./git-BhBtDGVR.mjs"; import path from "node:path"; import fs from "node:fs/promises"; import os from "node:os"; //#region src/agents/worktrees/checkout-git-config.ts const native = { sourceOnly: false, run: runGit, require: requireGit, worker: { text: runGitBytes, buffered: runGitBuffered }, withContentEnvironment: async (run) => await run(gitEnvironment()) }; const sharedDirectories = [ "objects", "refs", "logs", "worktrees", "reftable" ]; const sharedFiles = [ "packed-refs", "shallow", "info/exclude" ]; /** Only content operations borrow this view. Ref authority remains with the real repository. */ function usesCanonicalMetadata(args) { let i = 0; while (args[i] === "-c") i += 2; return [ "rev-parse", "show-ref", "update-ref", "branch", "fetch", "worktree" ].includes(args[i] ?? "") && !(args[i] === "worktree" && args[i + 1] === "remove" && !args.includes("--force") && !args.includes("-f")); } /** * Git reads repository config from GIT_COMMON_DIR, and config.worktree only when * that config enables extensions.worktreeConfig (Git config.c). Keep the actual * Git dir/index/HEAD and shared object/ref storage; substitute only trusted config * for this operation. No repository or operator config is changed or locked. */ async function withWorktreeGitConfig(cwd, sourceOnly, guard, run) { if (!sourceOnly) return await run(native); const check = () => { guard.signal?.throwIfAborted(); guard.beforeRun?.(); }; check(); const common = path.resolve(cwd, normalizeGitPathForFilesystem(await requireGit(cwd, ["rev-parse", "--git-common-dir"], guard))); const head = await requireGit(cwd, [ "rev-parse", "--verify", "HEAD^{commit}" ], guard); if (!/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u.test(head)) throw new Error("Unsupported Git object format"); const format = head.length === 64 ? "sha256" : "sha1"; const storage = await runGit(cwd, [ "config", "--local", "--get", "extensions.refStorage" ], guard); if (storage.code !== 0 && storage.code !== 1) throw createGitCommandError("git config refStorage", storage); const refStorage = storage.code === 1 ? "files" : storage.stdout.trim(); if (refStorage !== "files" && refStorage !== "reftable") throw new Error("Unsupported Git ref storage"); const settings = await runGit(cwd, [ "config", "--includes", "--null", "--get-regexp", "^core\\.(symlinks|ignorecase|precomposeunicode|excludesfile|autocrlf|eol)$" ], guard); if (settings.code !== 0 && settings.code !== 1) throw createGitCommandError("git config layout", settings); const layout = /* @__PURE__ */ new Map(); for (const field of settings.stdout.split("\0").filter(Boolean)) { const separator = field.indexOf("\n"); layout.set(separator < 0 ? field : field.slice(0, separator), separator < 0 ? "true" : field.slice(separator + 1)); } check(); const directory = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-git-config-")); let active = true; const pending = /* @__PURE__ */ new Set(); const assertActive = () => { check(); if (!active) throw new Error("Git configuration scope closed"); }; const prepareMetadata = async (target) => { for (const name of sharedDirectories) { assertActive(); const source = path.join(common, name); if (process.platform === "win32" && !await fs.stat(source).catch(() => void 0)) continue; await fs.symlink(source, path.join(target, name), process.platform === "win32" ? "junction" : "dir"); } await fs.mkdir(path.join(target, "info")); for (const name of sharedFiles) { assertActive(); const source = path.join(common, name); const destination = path.join(target, name); if (process.platform === "win32") await fs.copyFile(source, destination).catch((error) => { if (!hasNodeErrorCode(error, "ENOENT")) throw error; }); else await fs.symlink(source, destination, "file"); } }; try { await prepareMetadata(directory); await fs.writeFile(path.join(directory, "config"), `[core]\nrepositoryformatversion=${format === "sha256" || refStorage === "reftable" ? 1 : 0}\nbare=false\nfilemode=${process.platform !== "win32"}\nsymlinks=${process.platform !== "win32"}\n[extensions]\n${format === "sha256" ? "objectFormat=sha256\n" : ""}${refStorage === "reftable" ? "refStorage=reftable\n" : ""}`); for (const [key, value] of layout) await requireGit(cwd, [ "config", "--file", path.join(directory, "config"), key, value ], guard); const optionsFor = async (args, options) => { assertActive(); const guarded = { ...options, beforeRun: () => { assertActive(); options.beforeRun?.(); } }; if (usesCanonicalMetadata(args)) return guarded; let commandDirectory = directory; if (process.platform === "win32") { commandDirectory = await fs.mkdtemp(path.join(directory, "command-")); await prepareMetadata(commandDirectory); await fs.copyFile(path.join(directory, "config"), path.join(commandDirectory, "config")); } const inherited = mergeProcessEnv([options.baseEnv ?? process.env, options.env]); const env = Object.fromEntries(Object.entries(inherited).filter(([key]) => !key.toUpperCase().startsWith("GIT_"))); for (const [key, value] of Object.entries(options.env ?? {})) if (value !== void 0 && /^(?:GIT_INDEX_FILE|GIT_(?:AUTHOR|COMMITTER)_(?:NAME|EMAIL|DATE))$/u.test(key)) env[key] = value; Object.assign(env, { GIT_COMMON_DIR: commandDirectory, GIT_CONFIG_GLOBAL: gitNullConfigPath(), GIT_CONFIG_SYSTEM: gitNullConfigPath(), GIT_CONFIG_NOSYSTEM: "1", GIT_ATTR_NOSYSTEM: "1", GIT_NO_REPLACE_OBJECTS: "1", GIT_NO_LAZY_FETCH: "1" }); return { ...guarded, baseEnv: env, env }; }; const track = (operation) => { assertActive(); const result = operation(); pending.add(result); result.then(() => pending.delete(result), () => pending.delete(result)); return result; }; const scopedRun = (root, args, options) => track(async () => await runGit(root, args, await optionsFor(args, { ...options }))); const scopedRequire = async (root, args, options = {}) => { const result = await scopedRun(root, args, options); return requireGitCommandOutput(`git ${args.join(" ")}`, result).trim(); }; return await run({ sourceOnly: true, withContentEnvironment: (operation) => track(async () => { const options = await optionsFor(["pack-objects"], { env: {} }); return await operation(gitEnvironment(options.env)); }), run: scopedRun, require: scopedRequire, worker: { text: (root, args, options = {}) => track(async () => await runGitBytes(root, args, await optionsFor(args, options))), buffered: (root, args, options) => track(async () => await runGitBuffered(root, args, await optionsFor(args, { ...options }))) } }); } finally { active = false; await Promise.allSettled(pending); await fs.rm(directory, { recursive: true, force: true }); } } //#endregion export { withWorktreeGitConfig as t };