/** * `LocalSpillStore`: the host-filesystem implementation of the * `@deepseek-ai/dsh-spill` storage seam. Persists a tool's oversized text to a * private, session-scoped file (see `./store.ts` for the traversal-safe naming * and exclusive owner-only write) and returns a path locator plus local * read/grep retrieval guidance. * * @module @deepseek-ai/dsh-spill-local */ import { Context } from 'cordis' import { resolve } from 'node:path' import z from 'schemastery' import { SpillLocator, SpillStore } from '@deepseek-ai/dsh-spill' import type { SaveTextSpill, SpillRef } from '@deepseek-ai/dsh-spill' import { privateRoot, saveTextFile } from './store.ts' export { encodeSegment, privateRoot, saveTextFile, sessionDir } from './store.ts' export type { SavedText, SaveTextOptions } from './store.ts' /** Plugin config (all optional — `static Config` supplies the defaults). */ export interface Config { /** * Root directory for spill files. Omitted uses a lazily-created private * (0700) per-process directory under the OS temp dir — the safe default for * a local deployment. Set it to keep spill files under a known location. */ root?: string } /** * Local-filesystem spill backend. Files land under `/session-/…` * with unpredictable names, an exclusive owner-only (0600) write, and a private * (0700) root — a spilled tool result must not be readable by other local users * or redirectable via a planted symlink. */ export class LocalSpillStore extends SpillStore { static Config: z = z.object({ root: z.string(), }) /** Resolved absolute spill root (config `root`, else the private default), fixed at construction. */ readonly root: string constructor(ctx: Context, config: Config) { super(ctx) this.root = config.root !== undefined ? resolve(config.root) : privateRoot() } async saveText(input: SaveTextSpill): Promise { const saved = await saveTextFile({ root: this.root, sessionId: input.owner.sessionId, suggestedName: input.suggestedName, content: input.content, }) return { locator: SpillLocator(saved.path), bytes: saved.bytes, retrievalHint: 'Use read with offset/limit, or grep this path to search within it.', } } } export default LocalSpillStore