/** * Shared history-session listing for the SSH TUI: the launch picker's lazy * pages and the eager `listResumableSessions` listing use the same candidates, * labels, and ordering, so both surfaces offer the same sessions. */ import { listAttachableHosts } from './session-lock.js'; /** Delete one session's on-disk artifacts (log directory), best effort. */ /** * Delete one session's artifacts by id. * * `false` means the persistence layer could not locate it (already gone, or a * service that does not expose `locate`) — never an error worth surfacing, since * the only caller is tidying up on the way out. * @param persistence - the session persistence service. * @param id - the session id. * @returns true when a directory was found and removed. */ export declare function pruneSessionById(persistence: object | undefined, id: string): Promise; /** Last path segment for the footer chip (`\root\genshin\srv` → `srv`). */ export declare function sessionCwdLabel(cwd: string): string; export declare function formatFooterCwd(cwd: string): string; /** * Switch the process into a persisted session working directory. Returns the * directory actually used; missing/invalid paths stay put and are reported. * * The target must be an absolute directory. A regular file that happens to * exist at the recorded path used to pass `existsSync` and then fail inside * `chdir` with a platform errno; resume now refuses it before moving. */ export declare function enterSessionCwd(cwd: string | undefined, options?: { current?: string; exists?: (path: string) => boolean; isDirectory?: (path: string) => boolean; chdir?: (path: string) => void; }): { cwd: string; changed: boolean; error?: string; }; /** One selectable history session. */ export interface ResumableSession { id: string; label: string; updatedAt: number; cwd: string; /** Whether the full event log could not be inspected (corrupt/unsupported). */ unreadable?: boolean; /** Live Host that a new SSH can attach to. */ attach?: { pid: number; sock: string; state?: string; }; /** * `label` is a placeholder (the raw session id) because the log has not been * inspected yet. The picker holds these back instead of painting an id that * turns into a title two seconds later. */ labelPending?: boolean; } /** `MM-DD HH:mm` local-time label for session lists. */ export declare function formatSessionTime(timestamp: number): string; /** Incremental listing so a caller can paint before older logs are parsed. */ export interface ResumableSessionListing { /** Sessions already inspected (or restored from the disk cache). */ sessions: ResumableSession[]; /** Whether older logs are still being inspected. */ pending: boolean; } /** One lazy page: everything read so far, plus what is still uninspected. */ export interface ResumableSessionPage { /** Resolved sessions in display order; a label here is never a placeholder. */ sessions: ResumableSession[]; /** Candidates not inspected yet (an upper bound on rows still to come). */ remaining: number; /** No candidates are left to inspect. */ done: boolean; } /** * A listing that grows on demand. * * The picker reads one page, paints it, and only reads on when the user reaches * for older sessions. A launch used to inspect every log before the first * frame, which is what made a large history feel like a hang; the first page * alone also has every title resolved, so nothing on screen is ever a raw id * that turns into a title a second later. */ export interface ResumableSessionPager { /** Inspect onward until `size` more rows exist, or history runs out. */ page(size?: number): Promise; /** Read every remaining candidate (the eager listing wants the whole list). */ complete(): Promise; } /** Sessions read before the picker's first paint, and per lazy page after it. */ export declare const PICKER_PAGE_SIZE = 9; /** * Open a pager over the store. Nothing is inspected until the first `page()`. */ export declare function openResumableSessionPager(persistence: object, currentId: string, options?: { listHosts?: typeof listAttachableHosts; indexPath?: string; }): Promise; /** * Delete every session that never saw user input, and report what happened. * * The picker prunes these as it resolves them, but only for the surface that * looks at them: a launch that died before doing anything kept showing up in * *other* profiles' menus (the web session list reads the same artifacts without * this filter). So the same rule is offered as an explicit sweep, which is also * what `/cleanup` runs. * @param persistence - the session persistence service. * @param currentId - the live session, never a candidate for deletion. * @param options - `listHosts` for test injection. * @returns how many were deleted, how many were kept, and how many could not be * read (an unreadable log is kept: it might be a live writer). */ export declare function pruneBlankSessions(persistence: object, currentId: string, options?: { listHosts?: typeof listAttachableHosts; dryRun?: boolean; }): Promise<{ pruned: number; kept: number; unreadable: number; }>; /** * List resumable top-level sessions, newest first. * * Subagent-owned sessions and the current session are excluded. Sessions * whose event log cannot be inspected are kept (marked `unreadable`) instead * of silently disappearing from history; readable sessions with user input * sort first. The label is the persisted title, then the user's first input, * then the id. * @param persistence - the session persistence service. * @param currentId - the live session to exclude (empty at launch). * @returns every resumable candidate in display order (attachable live * hosts first, then readable logs, then unreadable). */ export declare function listResumableSessions(persistence: object, currentId: string, listHosts?: typeof listAttachableHosts): Promise; /** * List resumable sessions, painting the recent page first. * * `onUpdate` fires after the header sketch, after the first resolved title, and * again after each later inspect batch. Unchanged logs reuse * `$DSH_HOME/tui-session-index.json`. */ export declare function listResumableSessionsProgressive(persistence: object, currentId: string, options?: { listHosts?: typeof listAttachableHosts; onUpdate?: (listing: ResumableSessionListing) => void; indexPath?: string; priorityCount?: number; }): Promise;