import type { HostedNode, HostedNodeFile } from './types.js'; /** * Atomic JSON store for `hosted-nodes.json` — one row per hosted node. A single * file written to `.tmp` then renamed, with an in-memory cache keyed by id. * * **Persists the claim secret, or the invitation.** The orchestrator keeps nothing of * either (`state.json` omits every per-spawn env), so this row is what lets a respawn * start the child with the same secret, which keeps a QR code already shown valid and * lets a claimed node answer a rival `already-claimed`, and lets a `joining` node redeem * its invitation again. * * Concurrency assumption: only one cadre-host process owns a given rootDir * (the orchestrator already enforces this). No file-locking primitives. * * NOTE: every method here is **synchronous**, and `HostedNodeService` depends on * that for correctness, not just convenience — each of its long operations * re-reads a record and writes it back with no `await` in between, which is only * atomic against the event loop while `get`/`put` cannot yield. Giving this class * an async write path would silently reopen the resurrect-a-removed-node races * those re-reads exist to close, and the tests that guard them would still pass. */ export declare class HostedNodeStore { private readonly path; private cache; constructor(rootDir: string); filePath(): string; /** * Load (with caching). A missing file returns an empty store. A * present-but-malformed file throws rather than silently wiping — losing the * records would orphan live child processes (their claim secrets gone). */ load(): HostedNodeFile; /** Persist the current state atomically (write then rename). */ save(state: HostedNodeFile): void; /** Insert or replace a row. */ put(node: HostedNode): void; get(id: string): HostedNode | undefined; list(): HostedNode[]; remove(id: string): boolean; }