Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | 8x 8x 479x 478x 483x 483x 198x 109x 98x 97x 675x 389x 229x 244x 154x 154x 154x 154x 1046x 64x 64x 2x 11x 11x 11x 1x 498x 615x 615x 60x 295x 198x 94x 94x 94x 99x 136x 103x 103x 11x 10x 90x 90x 16x 16x 4x 3x 3x | /**
* Shared-root layout + per-vault registry + cross-process lock.
* One lilbee binary, one HF cache, many vault data-dirs, one active vault at a time.
*/
import { node } from "./binary-manager";
import { getDefaultLilbeeDataRoot } from "./session-token";
import {
DEFAULT_SHARED_CONFIG,
LOCK_STATE,
SHARED_PATH,
type ActiveLock,
type LockState,
type SharedConfig,
type VaultRegistryEntry,
} from "./types";
const VAULT_ID_BYTES = 6;
const FALLBACK_SHARED_ROOT = "/tmp/lilbee";
export function resolveSharedRoot(setting: string): string {
if (setting && setting.length > 0) return setting;
return getDefaultLilbeeDataRoot() ?? FALLBACK_SHARED_ROOT;
}
export function computeVaultId(vaultPath: string): string {
const canonical = node.resolve(vaultPath);
return node
.createHash("sha256")
.update(canonical)
.digest("hex")
.slice(0, VAULT_ID_BYTES * 2);
}
export function sharedBinDir(sharedRoot: string): string {
return node.join(sharedRoot, SHARED_PATH.BIN);
}
export function sharedModelsDir(sharedRoot: string): string {
return node.join(sharedRoot, SHARED_PATH.MODELS);
}
export function vaultsRootDir(sharedRoot: string): string {
return node.join(sharedRoot, SHARED_PATH.VAULTS);
}
export function defaultDataDirFor(sharedRoot: string, vaultId: string): string {
return node.join(vaultsRootDir(sharedRoot), vaultId);
}
function configPath(sharedRoot: string): string {
return node.join(sharedRoot, SHARED_PATH.CONFIG);
}
function registryPath(sharedRoot: string): string {
return node.join(sharedRoot, SHARED_PATH.REGISTRY);
}
function lockPath(sharedRoot: string): string {
return node.join(sharedRoot, SHARED_PATH.LOCK);
}
function ensureDir(path: string): void {
if (!node.existsSync(path)) node.mkdirSync(path, { recursive: true });
}
function writeJsonAtomic(path: string, value: unknown): void {
ensureDir(node.dirname(path));
const tmp = `${path}.tmp`;
node.writeFileSync(tmp, JSON.stringify(value, null, 2));
node.renameSync(tmp, path);
}
function readJson<T>(path: string): T | null {
if (!node.existsSync(path)) return null;
try {
return JSON.parse(node.readFileSync(path, "utf-8")) as T;
} catch {
return null;
}
}
function isProcessAlive(pid: number): boolean {
try {
node.processKill(pid, 0);
return true;
} catch {
return false;
}
}
/**
* Manages `<shared-root>/{config.json, registry.json, active.lock}`.
* All file I/O is synchronous because the call sites are plugin lifecycle
* events that already block on disk.
*/
export class VaultRegistry {
constructor(public readonly sharedRoot: string) {}
loadConfig(): SharedConfig {
const parsed = readJson<Partial<SharedConfig>>(configPath(this.sharedRoot));
return { ...DEFAULT_SHARED_CONFIG, ...(parsed ?? {}) };
}
saveConfig(config: SharedConfig): void {
writeJsonAtomic(configPath(this.sharedRoot), config);
}
list(): VaultRegistryEntry[] {
return readJson<VaultRegistryEntry[]>(registryPath(this.sharedRoot)) ?? [];
}
get(id: string): VaultRegistryEntry | null {
return this.list().find((e) => e.id === id) ?? null;
}
upsert(entry: VaultRegistryEntry): void {
const entries = this.list().filter((e) => e.id !== entry.id);
entries.push(entry);
writeJsonAtomic(registryPath(this.sharedRoot), entries);
}
/** Return the registered data-dir or the default location for this id. */
resolveDataDir(id: string): string {
return this.get(id)?.dataDir ?? defaultDataDirFor(this.sharedRoot, id);
}
readLock(): ActiveLock | null {
return readJson<ActiveLock>(lockPath(this.sharedRoot));
}
/**
* Classify the current lock against our vault id. STALE means a lock file
* exists but the owning PID is gone — safe to take.
*/
lockState(vaultId: string): LockState {
const lock = this.readLock();
if (lock === null) return LOCK_STATE.NONE;
if (!isProcessAlive(lock.pid)) return LOCK_STATE.STALE;
return lock.vaultId === vaultId ? LOCK_STATE.OURS : LOCK_STATE.LIVE_OTHER;
}
/**
* Atomically write a lock claiming the shared root for *vaultId*.
* Caller must already have decided the existing lock (if any) is takeable
* (STALE, OURS, or LIVE_OTHER after the user authorized take-over).
*/
writeLock(lock: ActiveLock): void {
ensureDir(this.sharedRoot);
node.writeFileSync(lockPath(this.sharedRoot), JSON.stringify(lock, null, 2));
}
releaseLock(vaultId: string): void {
const lock = this.readLock();
if (lock === null) return;
if (lock.vaultId !== vaultId) return;
try {
node.unlinkSync(lockPath(this.sharedRoot));
} catch {
// already gone — fine
}
}
}
|