Files
2026-07-13 12:20:32 +08:00

95 lines
2.9 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const QEMU_HOST_GATEWAY = "10.0.2.2";
// Default location of the per-agent .env inside the VM. The open-computer CLI scp's
// agents/<name>/.env here on boot so each agent VM can deterministically
// override values from the service-level .env.
const DEFAULT_AGENT_ENV = "/home/agent/agent.env";
function parseEnvFile(envPath) {
const entries = [];
if (!fs.existsSync(envPath)) return entries;
for (const line of fs.readFileSync(envPath, "utf8").split("\n")) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;
const eq = trimmed.indexOf("=");
if (eq === -1) continue;
const key = trimmed.slice(0, eq).trim();
const val = trimmed
.slice(eq + 1)
.trim()
.replace(/^["']|["']$/g, "");
if (key) entries.push([key, val]);
}
return entries;
}
// Load the service-level .env. Only fills keys that are not already set in the
// environment (so explicitly exported env vars / systemd Environment= win).
// Returns { path, keys } where `keys` is the set of keys this call populated,
// so a later agent .env can override exactly those without clobbering values
// that were set externally.
function loadDotEnv(serviceRoot) {
const envPath = path.join(serviceRoot, ".env");
const exists = fs.existsSync(envPath);
const keys = new Set();
for (const [key, val] of parseEnvFile(envPath)) {
if (process.env[key] === undefined) {
process.env[key] = val;
keys.add(key);
}
}
return { path: exists ? envPath : null, keys };
}
// Load a per-agent .env on top of the service .env. Values for keys that came
// from the service .env are overridden; brand-new keys are filled. Values that
// were set externally (e.g. by systemd) are respected and never clobbered,
// so agent .env cannot break VM-critical vars like PORT.
function loadAgentEnv(envPath, serviceKeys) {
const resolved = envPath || process.env.OPEN_COMPUTER_AGENT_ENV || DEFAULT_AGENT_ENV;
const entries = parseEnvFile(resolved);
if (!entries.length && !fs.existsSync(resolved)) return null;
for (const [key, val] of entries) {
if (serviceKeys && serviceKeys.has(key)) {
process.env[key] = val;
} else if (process.env[key] === undefined) {
process.env[key] = val;
}
}
return resolved;
}
function envFlag(name) {
return ["1", "true", "yes", "on"].includes(
String(process.env[name] || "").toLowerCase(),
);
}
function normalizeBaseUrl(url) {
if (!url) return "";
let u = url.replace(/\/+$/, "");
if (!u.endsWith("/v1")) u += "/v1";
return u;
}
function resolveBaseUrlForGuest(url) {
if (!url) return "";
return normalizeBaseUrl(url).replace(
/localhost|127\.0\.0\.1/g,
QEMU_HOST_GATEWAY,
);
}
module.exports = {
DEFAULT_AGENT_ENV,
QEMU_HOST_GATEWAY,
envFlag,
loadAgentEnv,
loadDotEnv,
normalizeBaseUrl,
resolveBaseUrlForGuest,
};