/** * 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)); }