UNPKG

naisys

Version:

NAISYS - Autonomous AI agent runner with built-in context management and cost tracking

129 lines 5.03 kB
import { buildClientConfig } from "@naisys/common"; import { ConfigResponseSchema, HubEvents } from "@naisys/hub-protocol"; import dotenv from "dotenv"; import fs from "fs"; import { readFile } from "fs/promises"; import os from "os"; import path from "path"; import * as pathService from "./services/runtime/pathService.js"; export function createGlobalConfig(hubClient, supervisorUrl) { let cachedConfig; let configReadyPromise; let configChangedHandler; init(); function init() { if (hubClient) { let resolveConfig; let rejectConfig; configReadyPromise = new Promise((resolve, reject) => { resolveConfig = resolve; rejectConfig = reject; }); hubClient.registerEvent(HubEvents.VARIABLES_UPDATED, async (data) => { try { const response = ConfigResponseSchema.parse(data); if (!response.success || !response.config) { rejectConfig(new Error(response.error || "Failed to get config from hub")); return; } cachedConfig = await appendClientConfig(response.config); resolveConfig(); configChangedHandler?.(); } catch (error) { rejectConfig(error instanceof Error ? error : new Error(String(error))); } }); } else { const { parsed: dotenvVars } = dotenv.config({ quiet: true }); const clientConfig = buildClientConfig(dotenvVars ?? {}); configReadyPromise = appendClientConfig(clientConfig).then((config) => { cachedConfig = config; }); } } async function appendClientConfig(clientConfig) { /** Identifies this runner - shows after @ in prompt, used for multi-machine host identification */ const hostname = process.env.NAISYS_HOSTNAME || os.hostname(); const packageVersion = await getVersion(); return { ...clientConfig, hostname, packageVersion, supervisorUrl, }; } async function getVersion() { const installPath = pathService.getInstallPath(); const packageJsonPath = path.join(installPath, "package.json"); const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8")); return packageJson.version; } /** Patch process.env and the cached config so runtime changes are visible. */ function patchRuntimeVariable(key, value, options = {}) { process.env[key] = value; // Patch cached config so runtime credential refreshes are visible // immediately without waiting for a process restart. if (cachedConfig) { cachedConfig.variableMap[key] = value; const exportToShell = options.exportToShell ?? (!hubClient || key in cachedConfig.shellVariableMap); if (exportToShell) { cachedConfig.shellVariableMap[key] = value; } else { delete cachedConfig.shellVariableMap[key]; } if (key === "NAISYS_HOSTNAME") { cachedConfig.hostname = value; } } } /** * Update a single key in the .env file, preserving comments and ordering. * If the key exists, its value is replaced in-place. * If the key does not exist, it is appended to the end of the file. * Also updates process.env so the change is visible immediately. */ function setVariableValue(key, value, options = {}) { const dotenvPath = path.resolve(".env"); const content = fs.existsSync(dotenvPath) ? fs.readFileSync(dotenvPath, "utf-8") : ""; const lines = content.split("\n"); let found = false; for (let i = 0; i < lines.length; i++) { const trimmed = lines[i].trim(); if (!trimmed || trimmed.startsWith("#")) continue; const eqIdx = trimmed.indexOf("="); if (eqIdx === -1) continue; const lineKey = trimmed.substring(0, eqIdx).trim(); if (lineKey === key) { lines[i] = `${key}=${value}`; found = true; break; } } if (!found) { lines.push(`${key}=${value}`); } fs.writeFileSync(dotenvPath, lines.join("\n")); patchRuntimeVariable(key, value, options); } function updateEnvValue(key, value) { setVariableValue(key, value); } return { globalConfig: () => cachedConfig, waitForConfig: () => configReadyPromise, onConfigChanged: (handler) => { configChangedHandler = handler; }, setVariableValue, updateEnvValue, }; } //# sourceMappingURL=globalConfig.js.map