/** * Headless Operation Handler * * Handles notebook operations when no UI is connected to Nebula. * Mirrors useOperationHandler (React) but operates on files instead of UI state. */ import { FilesystemService } from '../fs/fs-service'; import { KernelService } from '../kernel/kernel-service'; import { UpdateSummary } from './undoRedoManager'; interface OperationResult { success: boolean; error?: string; [key: string]: unknown; } interface AgentLockInfo { agentId: string; clientName?: string; clientVersion?: string; expiresAt: number; lockedAt: number; } interface OperationRouterInterface { startAgentSession: (path: string, agentId: string, metadata?: { clientName?: string; clientVersion?: string; }) => { success: boolean; error?: string; lock?: AgentLockInfo; }; endAgentSession: (path: string, agentId: string) => { success: boolean; error?: string; }; } export declare class HeadlessOperationHandler { private fsService; private kernelService; private operationRouter; private undoRedoManager; private cache; private writeLocks; constructor(fsService: FilesystemService, operationRouter?: OperationRouterInterface, kernelService?: KernelService); /** * Get notebook from cache, loading from disk if needed. */ private getCachedNotebook; /** In-flight cache warm-ups per notebook path (single-flight). */ private warming; /** * Warm the notebook + undo-history caches with non-blocking I/O. The sync * fallback in getCachedNotebook blocks the event loop on a cache miss — on * a network filesystem a large notebook + history read stalls every * in-flight request (kernel completions included) for seconds. Entry points * await this first so the sync accessors below are pure in-memory hits. */ private ensureCached; private getCells; private getVisibleOutputs; private saveCells; /** * Persist dirty notebooks to disk. */ flush(notebookPath?: string): Promise; /** In-flight persist per notebook path (single-flight). */ private persisting; private asyncPersist; private schedulePersist; /** * Invalidate cache for a notebook. */ invalidate(notebookPath?: string): void; /** * Check if notebook has unsaved changes. */ isDirty(notebookPath: string): boolean; /** * Check if agent has permission to modify this notebook. */ private checkAgentPermission; /** * Compare expectedHash/expectedHashes stamped by the operation router * against current cell content. Returns a conflict result, or null to * proceed. The conflict message carries the current content so the agent * can re-apply its intent without an extra read. */ private checkExpectedHashes; /** * Apply a notebook operation. */ applyOperation(operation: Record): Promise; /** * Read notebook from cache with optional output truncation. */ readNotebook(notebookPath: string, includeOutputs?: boolean, maxLines?: number, maxChars?: number, maxLinesError?: number, maxCharsError?: number): Promise; private truncateCellOutputs; private truncateOutput; private insertCell; private deleteCell; private updateContent; private updateMetadata; private moveCell; private duplicateCell; private updateOutputs; private createNotebook; private readCell; private readCellOutput; private sleep; private saveOutputToTempFile; private clearNotebook; private deleteCells; private insertCells; private searchCells; private clearOutputs; /** * Execute a cell using the kernel service. * Matches Python headless_handler._execute_cell() behavior. */ private executeCell; /** * Handle undo operation. */ private handleUndo; /** * Handle redo operation. */ private handleRedo; /** * Record an undoable operation (helper method for other operations). */ private recordUndoableOperation; /** * Record a non-undoable log operation (helper method for other operations). */ private recordLogOperation; /** * Get updates since a timestamp (public method for operation router). * Used by startAgentSession to inform agent what changed between sessions. */ getUpdatesSince(notebookPath: string, sinceTimestamp: number): UpdateSummary[]; /** * Get-or-create the execution session for a notebook: on its bound cluster * server when a binding exists (proxied session id), locally otherwise. * A dead binding falls back to a fresh local kernel — allocations end * routinely (walltime, cancel), and that fallback is the documented * "falls back to a new kernel on next use" behavior. */ private resolveKernelSession; /** Start (get-or-create) a kernel on the bound server; null if it is gone. */ private startBoundKernel; private startKernelOp; private shutdownKernelOp; private restartKernelOp; private interruptKernelOp; } export {};