| 1 | // Shared local-dev state written by tools/lchat-dev-up.mjs and read back by it |
| 2 | // (to reuse the same admin token / db / port across restarts) and by |
| 3 | // tools/lchat-dev-bot.mjs (to auto-discover the running server's base URL + |
| 4 | // admin token, so you don't have to copy them by hand). |
| 5 | // |
| 6 | // The file lives at the repo root as `.lchat-dev.json` and is gitignored — it |
| 7 | // holds the instance's admin token, so treat it like the secret it is (it's |
| 8 | // written 0600 and never committed). The SQLite db it points at lives next to |
| 9 | // it as `.lchat-dev.db` (+ `-wal`/`-shm`) and is covered by the repo's `*.db` |
| 10 | // ignore rules. |
| 11 | import fs from "node:fs"; |
| 12 | import path from "node:path"; |
| 13 | import { fileURLToPath } from "node:url"; |
| 14 | |
| 15 | const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); |
| 16 | |
| 17 | export const CONFIG_PATH = path.join(REPO_ROOT, ".lchat-dev.json"); |
| 18 | export const DEFAULT_DB_PATH = path.join(REPO_ROOT, ".lchat-dev.db"); |
| 19 | |
| 20 | // Where tools/lchat-dev-bot.mjs keeps the dev bot's identity (its keypair + |
| 21 | // device token) so it survives restarts like the server does. It's tied to the |
| 22 | // db above — a fresh instance invalidates it — so lchat-dev-up clears it on any |
| 23 | // fresh start, and lchat-dev-bot --clean drops it to re-provision. |
| 24 | export const BOT_SESSION_PATH = path.join(REPO_ROOT, ".lchat-dev-bot.json"); |
| 25 | |
| 26 | // Where the dev hooks sidecar (lchat hooks run, spawned by lchat-dev-up) |
| 27 | // persists its service-identity session. Tied to the db exactly like the bot |
| 28 | // session: a fresh instance invalidates it, so lchat-dev-up clears it on any |
| 29 | // fresh start and re-mints the service invite. |
| 30 | export const HOOKS_SESSION_PATH = path.join(REPO_ROOT, ".lchat-dev-hooks.json"); |
| 31 | |
| 32 | // The three files SQLite writes for one WAL-mode database, so callers can clean |
| 33 | // them all in one sweep. |
| 34 | export function dbFiles(dbPath) { |
| 35 | return [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]; |
| 36 | } |
| 37 | |
| 38 | // Returns the parsed config, or null if it's missing/unreadable. |
| 39 | export function readConfig() { |
| 40 | try { |
| 41 | return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8")); |
| 42 | } catch { |
| 43 | return null; |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | export function writeConfig(cfg) { |
| 48 | fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + "\n", { mode: 0o600 }); |
| 49 | } |
| 50 | |
| 51 | export function clearConfig() { |
| 52 | fs.rmSync(CONFIG_PATH, { force: true }); |
| 53 | } |
| 54 | |
| 55 | export function clearBotSession() { |
| 56 | fs.rmSync(BOT_SESSION_PATH, { force: true }); |
| 57 | } |
| 58 | |
| 59 | export function clearHooksSession() { |
| 60 | fs.rmSync(HOOKS_SESSION_PATH, { force: true }); |
| 61 | } |
| 62 | |