/**
* Copyright (c) 2026, Salesforce, Inc.,
* All rights reserved.
* For full license text, see the LICENSE.txt file
*/
import crypto from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
/**
* Resolves the graphiti home directory at call time. Re-reads
* `GRAPHITI_HOME` on every call so tests that mutate the env var after
* module load (or fixtures that set it before each call) see the current
* value rather than a frozen snapshot from import time.
*
* Single source of truth for every graphiti file/directory path: schema
* cache, sessions, ObjectInfo cache, the active-session pointer.
*/
export function graphitiHome(): string {
return process.env.GRAPHITI_HOME ?? path.join(os.homedir(), ".graphiti");
}
/**
* Write `text` to `finalPath` atomically. Writes to a sibling
* `.tmp...` file, then renames over the final
* path. Concurrent readers see either the old contents or the fully-written
* new contents — never a partial. The temp file is unlinked if the rename
* fails, so failure paths do not leak partial files.
*/
export function atomicWriteText(finalPath: string, text: string): void {
fs.mkdirSync(path.dirname(finalPath), { recursive: true });
const tmp = `${finalPath}.tmp.${process.pid}.${Date.now()}.${crypto.randomBytes(4).toString("hex")}`;
fs.writeFileSync(tmp, text, "utf-8");
try {
fs.renameSync(tmp, finalPath);
} catch (e) {
try {
fs.unlinkSync(tmp);
} catch {
// best-effort cleanup
}
throw e;
}
}
/** Write JSON to `finalPath` atomically (pretty-printed). See {@link atomicWriteText}. */
export function atomicWriteJson(finalPath: string, payload: unknown): void {
atomicWriteText(finalPath, JSON.stringify(payload, null, 2));
}