openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
95 lines (94 loc) • 3.46 kB
JavaScript
import { o as resolveRequiredHomeDir } from "./home-dir-BjcCg_IW.js";
import { a as normalizeEnvVarKey } from "./host-env-security-RnaFYKhk.js";
import { d as resolveConfigDir } from "./utils-CCC-BEJH.js";
import { t as createSubsystemLogger } from "./subsystem-BzXSmsuh.js";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import dotenv from "dotenv";
//#region src/infra/dotenv-global.ts
const logger = createSubsystemLogger("infra:dotenv");
function readGlobalRuntimeDotEnvFile(params) {
let content;
try {
content = fs.readFileSync(params.filePath, "utf8");
} catch (error) {
if (!params.quiet) {
if ((error && typeof error === "object" && "code" in error ? String(error.code) : void 0) !== "ENOENT") logger.warn(`Failed to read ${params.filePath}: ${String(error)}`, { error });
}
return null;
}
let parsed;
try {
parsed = dotenv.parse(content);
} catch (error) {
if (!params.quiet) logger.warn(`Failed to parse ${params.filePath}: ${String(error)}`, { error });
return null;
}
const entries = [];
for (const [rawKey, value] of Object.entries(parsed)) {
const key = normalizeEnvVarKey(rawKey, { portable: true });
if (key) entries.push({
key,
value
});
}
return {
filePath: params.filePath,
entries
};
}
function loadParsedDotEnvFiles(files) {
const preExistingKeys = new Set(Object.keys(process.env));
const conflicts = /* @__PURE__ */ new Map();
const firstSeen = /* @__PURE__ */ new Map();
for (const file of files) for (const { key, value } of file.entries) {
if (preExistingKeys.has(key)) continue;
const previous = firstSeen.get(key);
if (previous) {
if (previous.value !== value) {
const conflictKey = `${previous.filePath}\u0000${file.filePath}`;
const existing = conflicts.get(conflictKey);
if (existing) existing.keys.add(key);
else conflicts.set(conflictKey, {
keptPath: previous.filePath,
ignoredPath: file.filePath,
keys: new Set([key])
});
}
continue;
}
firstSeen.set(key, {
value,
filePath: file.filePath
});
if (process.env[key] === void 0) process.env[key] = value;
}
for (const conflict of conflicts.values()) {
const keys = [...conflict.keys].toSorted();
if (keys.length === 0) continue;
logger.warn(`Conflicting values in ${conflict.keptPath} and ${conflict.ignoredPath} for ${keys.join(", ")}; keeping ${conflict.keptPath}.`, {
keptPath: conflict.keptPath,
ignoredPath: conflict.ignoredPath,
keys
});
}
}
/** Load global runtime dotenv files into `process.env` with first-wins precedence. */
function loadGlobalRuntimeDotEnvFiles(opts) {
const quiet = opts?.quiet ?? true;
const stateEnvPath = opts?.stateEnvPath ?? path.join(resolveConfigDir(process.env), ".env");
const defaultStateEnvPath = path.join(resolveRequiredHomeDir(process.env, os.homedir), ".openclaw", ".env");
const hasExplicitNonDefaultStateDir = process.env.OPENCLAW_STATE_DIR?.trim() !== void 0 && path.resolve(stateEnvPath) !== path.resolve(defaultStateEnvPath);
const parsedFiles = [readGlobalRuntimeDotEnvFile({
filePath: stateEnvPath,
quiet
})];
if (!hasExplicitNonDefaultStateDir) parsedFiles.push(readGlobalRuntimeDotEnvFile({
filePath: path.join(resolveRequiredHomeDir(process.env, os.homedir), ".config", "openclaw", "gateway.env"),
quiet
}));
loadParsedDotEnvFiles(parsedFiles.filter((file) => file !== null));
}
//#endregion
export { loadGlobalRuntimeDotEnvFiles as t };