@itookit/dsht 0.2.4 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +54 -25
- package/README.zh.md +54 -25
- package/dist/catalog/controller.d.ts +32 -0
- package/dist/catalog/controller.js +88 -0
- package/dist/catalog/index.d.ts +2 -0
- package/dist/catalog/index.js +2 -0
- package/dist/{cli.js → cli/index.js} +40 -24
- package/dist/controller/connection.d.ts +80 -0
- package/dist/controller/connection.js +190 -0
- package/dist/controller/controller.d.ts +260 -0
- package/dist/controller/controller.js +362 -0
- package/dist/controller/index.d.ts +5 -0
- package/dist/controller/index.js +3 -0
- package/dist/controller/memory-log.d.ts +35 -0
- package/dist/controller/memory-log.js +95 -0
- package/dist/cost/controller.d.ts +34 -0
- package/dist/cost/controller.js +107 -0
- package/dist/cost/index.d.ts +9 -0
- package/dist/cost/index.js +7 -0
- package/dist/cost/ledger-files.d.ts +20 -0
- package/dist/cost/ledger-files.js +128 -0
- package/dist/cost/ledger.d.ts +65 -0
- package/dist/cost/ledger.js +136 -0
- package/dist/cost/pricing.d.ts +48 -0
- package/dist/cost/pricing.js +141 -0
- package/dist/cost/records.d.ts +17 -0
- package/dist/cost/records.js +66 -0
- package/dist/cost/scanner.d.ts +22 -0
- package/dist/cost/scanner.js +95 -0
- package/dist/cost/types.d.ts +69 -0
- package/dist/cost/types.js +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/session/connection-view.d.ts +18 -0
- package/dist/session/connection-view.js +1 -0
- package/dist/{controller.d.ts → session/controller.d.ts} +119 -137
- package/dist/session/controller.js +616 -0
- package/dist/session/export-html.d.ts +9 -0
- package/dist/session/export-html.js +39 -0
- package/dist/session/export.d.ts +9 -0
- package/dist/session/export.js +38 -0
- package/dist/{history.d.ts → session/history.d.ts} +18 -1
- package/dist/session/history.js +338 -0
- package/dist/session/index.d.ts +17 -0
- package/dist/session/index.js +10 -0
- package/dist/session/markdown.d.ts +39 -0
- package/dist/session/markdown.js +255 -0
- package/dist/session/math.d.ts +11 -0
- package/dist/session/math.js +82 -0
- package/dist/{navigation.d.ts → session/navigation.d.ts} +1 -1
- package/dist/{navigation.js → session/navigation.js} +1 -1
- package/dist/{references.js → session/references.js} +1 -1
- package/dist/{telemetry.d.ts → session/telemetry.d.ts} +12 -1
- package/dist/{telemetry.js → session/telemetry.js} +18 -4
- package/dist/{transcript.d.ts → session/transcript.d.ts} +23 -1
- package/dist/{transcript.js → session/transcript.js} +36 -18
- package/dist/session/types.d.ts +18 -0
- package/dist/session/types.js +2 -0
- package/dist/state.d.ts +41 -0
- package/dist/state.js +9 -0
- package/dist/storage/directories.d.ts +14 -0
- package/dist/storage/directories.js +24 -0
- package/dist/storage/files.d.ts +43 -0
- package/dist/storage/files.js +135 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.js +3 -0
- package/dist/transport/auth.js +65 -0
- package/dist/{client.d.ts → transport/client.d.ts} +8 -1
- package/dist/{client.js → transport/client.js} +21 -4
- package/dist/transport/host.d.ts +13 -0
- package/dist/transport/host.js +1 -0
- package/dist/ui/app.d.ts +11 -0
- package/dist/ui/app.js +726 -0
- package/dist/ui/chat/header.d.ts +11 -0
- package/dist/ui/chat/header.js +14 -0
- package/dist/{history-view.d.ts → ui/chat/history-view.d.ts} +1 -1
- package/dist/{history-view.js → ui/chat/history-view.js} +3 -3
- package/dist/ui/chat/status.d.ts +79 -0
- package/dist/ui/chat/status.js +311 -0
- package/dist/ui/chat/viewport.d.ts +17 -0
- package/dist/ui/chat/viewport.js +14 -0
- package/dist/ui/commands/parse.d.ts +96 -0
- package/dist/ui/commands/parse.js +121 -0
- package/dist/ui/commands/registry.d.ts +33 -0
- package/dist/ui/commands/registry.js +72 -0
- package/dist/ui/copy-mode.d.ts +4 -0
- package/dist/ui/copy-mode.js +6 -0
- package/dist/{cost-view.d.ts → ui/dialogs/cost.d.ts} +1 -1
- package/dist/{cost-view.js → ui/dialogs/cost.js} +3 -3
- package/dist/ui/dialogs/index.d.ts +120 -0
- package/dist/ui/dialogs/index.js +113 -0
- package/dist/ui/dialogs/picker.d.ts +18 -0
- package/dist/ui/dialogs/picker.js +38 -0
- package/dist/ui/frozen.d.ts +8 -0
- package/dist/ui/frozen.js +7 -0
- package/dist/ui/input/references.d.ts +12 -0
- package/dist/ui/input/references.js +15 -0
- package/dist/ui/mount.d.ts +6 -0
- package/dist/ui/mount.js +11 -0
- package/dist/{theme.d.ts → ui/theme/index.d.ts} +1 -1
- package/dsht-m.png +0 -0
- package/package.json +20 -14
- package/dist/app.d.ts +0 -21
- package/dist/app.js +0 -746
- package/dist/auth.js +0 -108
- package/dist/controller.js +0 -923
- package/dist/cost.d.ts +0 -119
- package/dist/cost.js +0 -313
- package/dist/history.js +0 -177
- package/dist/status.d.ts +0 -28
- package/dist/status.js +0 -157
- package/dsht.png +0 -0
- /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
- /package/dist/{memory.d.ts → session/memory.d.ts} +0 -0
- /package/dist/{memory.js → session/memory.js} +0 -0
- /package/dist/{references.d.ts → session/references.d.ts} +0 -0
- /package/dist/{auth.d.ts → transport/auth.d.ts} +0 -0
- /package/dist/{endpoint.d.ts → transport/endpoint.d.ts} +0 -0
- /package/dist/{endpoint.js → transport/endpoint.js} +0 -0
- /package/dist/{wire.d.ts → transport/wire.d.ts} +0 -0
- /package/dist/{wire.js → transport/wire.js} +0 -0
- /package/dist/{input-history.d.ts → ui/input/history.d.ts} +0 -0
- /package/dist/{input-history.js → ui/input/history.js} +0 -0
- /package/dist/{input.d.ts → ui/input/input.d.ts} +0 -0
- /package/dist/{input.js → ui/input/input.js} +0 -0
- /package/dist/{mouse.d.ts → ui/input/mouse.d.ts} +0 -0
- /package/dist/{mouse.js → ui/input/mouse.js} +0 -0
- /package/dist/{theme.js → ui/theme/index.js} +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/** Application facade: composes the connection, session, catalog and cost domains. */
|
|
2
|
+
import { Client } from '../transport/client.ts';
|
|
3
|
+
import { type Json, type ObjectValue } from '../transport/wire.ts';
|
|
4
|
+
import { type HistoryLimits } from '../session/memory.ts';
|
|
5
|
+
import { SessionController } from '../session/controller.ts';
|
|
6
|
+
import { CatalogController } from '../catalog/controller.ts';
|
|
7
|
+
import type { Telemetry } from '../session/telemetry.ts';
|
|
8
|
+
import type { Transcript } from '../session/transcript.ts';
|
|
9
|
+
import type { CostLedger } from '../cost/ledger.ts';
|
|
10
|
+
import { CostController } from '../cost/controller.ts';
|
|
11
|
+
import { ConnectionController, type ConnectionListener } from './connection.ts';
|
|
12
|
+
import { MemoryLog } from './memory-log.ts';
|
|
13
|
+
import { type ControllerStore, type State } from '../state.ts';
|
|
14
|
+
import type { HistorySearch, RemovalTarget } from '../session/types.ts';
|
|
15
|
+
/** Application facade over the domain controllers; the UI owns only this object.
|
|
16
|
+
*
|
|
17
|
+
* State lives here, connection generations live in `connection`, the selected session and its
|
|
18
|
+
* history live in `session`, model metadata lives in `catalog`, and billing lives in `cost`.
|
|
19
|
+
*/
|
|
20
|
+
export declare class Controller implements ControllerStore, ConnectionListener {
|
|
21
|
+
readonly base: string;
|
|
22
|
+
private readonly initialSession?;
|
|
23
|
+
readonly costs?: CostLedger | undefined;
|
|
24
|
+
readonly historyLimits: HistoryLimits;
|
|
25
|
+
readonly memoryLogPath?: string | undefined;
|
|
26
|
+
state: State;
|
|
27
|
+
/** Physical connection, retry loop and projection store. */
|
|
28
|
+
readonly connection: ConnectionController;
|
|
29
|
+
/** Selected session, follow stream, history and interactions. */
|
|
30
|
+
readonly session: SessionController;
|
|
31
|
+
/** Model routes and agent-preset metadata. */
|
|
32
|
+
readonly catalog: CatalogController;
|
|
33
|
+
/** Background billing scan; present only when a ledger was supplied. */
|
|
34
|
+
readonly cost: CostController | undefined;
|
|
35
|
+
/** Bounded runtime memory samples; present only when a log path was supplied. */
|
|
36
|
+
readonly memoryLog: MemoryLog | undefined;
|
|
37
|
+
private readonly observers;
|
|
38
|
+
private selector;
|
|
39
|
+
constructor(base: string, token: string | undefined, initialSession?: string | undefined, makeClient?: () => Client, authenticate?: (client: Client) => Promise<void>, costs?: CostLedger | undefined, historyLimits?: HistoryLimits, memoryLogPath?: string | undefined);
|
|
40
|
+
/** React-compatible state subscription. */
|
|
41
|
+
subscribe: (listener: () => void) => (() => void);
|
|
42
|
+
/** Snapshot identity changes only when the controller publishes. */
|
|
43
|
+
snapshot: () => State;
|
|
44
|
+
/** Current projection store; replaced at each connection generation. */
|
|
45
|
+
get telemetry(): Telemetry;
|
|
46
|
+
/** @returns The current selector generation. */
|
|
47
|
+
selection(): number;
|
|
48
|
+
/** Advance the selector generation when the selected workspace or session changes. */
|
|
49
|
+
bumpSelection(): void;
|
|
50
|
+
/** Publish a state patch, re-deriving the visible pending interactions.
|
|
51
|
+
* @param patch - Fields to replace on the current state.
|
|
52
|
+
*/
|
|
53
|
+
update(patch: Partial<State>): void;
|
|
54
|
+
/** Start one retry loop, with a fresh snapshot generation after every disconnect. */
|
|
55
|
+
start(): void;
|
|
56
|
+
/** Cancel retries and HTTP, close the socket, and release session and catalog work. */
|
|
57
|
+
stop(): Promise<void>;
|
|
58
|
+
/** Stop the selected turn and then close, so quitting does not leave host work running.
|
|
59
|
+
* An idle session stays untouched, and an in-flight cancellation is awaited rather than repeated.
|
|
60
|
+
*/
|
|
61
|
+
shutdown(): Promise<void>;
|
|
62
|
+
/** Run a UI operation and expose errors without destroying the current input.
|
|
63
|
+
* @param operation - Operation to run while the client is busy.
|
|
64
|
+
* @returns Whether the operation completed.
|
|
65
|
+
*/
|
|
66
|
+
perform(operation: () => Promise<void>): Promise<boolean>;
|
|
67
|
+
/** Read the counters one memory sample records; content never leaves as text.
|
|
68
|
+
*
|
|
69
|
+
* The retained transcript and the ledger are small in practice, so a sample also reads the two
|
|
70
|
+
* structures that grow with rendered content — the layout row cache and the bounded math and
|
|
71
|
+
* diagram cache — and the work the last cost scan re-read, which is the only timer here whose
|
|
72
|
+
* per-pass work scales with history. With a runtime that exposes `gc`, the sample also reports
|
|
73
|
+
* the heap after a forced collection, so retained state and uncollected garbage stay distinct.
|
|
74
|
+
*/
|
|
75
|
+
private memorySample;
|
|
76
|
+
/** Collect before reading the heap when the runtime exposes a collection.
|
|
77
|
+
* @returns Heap in use after the collection and how long it took, or undefined without `global.gc`.
|
|
78
|
+
*/
|
|
79
|
+
private forcedGc;
|
|
80
|
+
/** A new generation starts; drop generation-scoped domain state. */
|
|
81
|
+
begin(): void;
|
|
82
|
+
/** The event stream is ready and the control baseline is applied. */
|
|
83
|
+
ready(): Promise<void>;
|
|
84
|
+
/** The generation ended; invalidate session work and stop the scan. */
|
|
85
|
+
ended(): Promise<void>;
|
|
86
|
+
/** Deliver a host waterfall to the session domain.
|
|
87
|
+
* @param frame - One decoded waterfall frame.
|
|
88
|
+
* @returns Whether the session domain retained it.
|
|
89
|
+
*/
|
|
90
|
+
waterfall(frame: ObjectValue): boolean;
|
|
91
|
+
/** Drop a waterfall the host cancelled.
|
|
92
|
+
* @param eventId - Correlation id previously retained.
|
|
93
|
+
*/
|
|
94
|
+
cancelled(eventId: string): void;
|
|
95
|
+
/** Apply one host running-state notification.
|
|
96
|
+
* @param sessionId - Session whose state changed.
|
|
97
|
+
* @param running - Whether the host still runs that session.
|
|
98
|
+
*/
|
|
99
|
+
status(sessionId: string, running: boolean): void;
|
|
100
|
+
/** Surface a host-reported session error.
|
|
101
|
+
* @param sessionId - Session the host reported on.
|
|
102
|
+
* @param error - Error payload as delivered by the host.
|
|
103
|
+
*/
|
|
104
|
+
error(sessionId: unknown, error: unknown): void;
|
|
105
|
+
/** Reload the model catalog after a host settings, credential or adapter change. */
|
|
106
|
+
invalidated(): void;
|
|
107
|
+
/** Refresh billing after a turn finished. */
|
|
108
|
+
idle(): void;
|
|
109
|
+
/** @returns Host running state of the selected session. */
|
|
110
|
+
get running(): boolean;
|
|
111
|
+
/** @returns Current session title, falling back to the list title and then the ID. */
|
|
112
|
+
get sessionName(): string | undefined;
|
|
113
|
+
/** @returns Current agent-preset label. */
|
|
114
|
+
get sessionMode(): string | undefined;
|
|
115
|
+
/** @returns Epoch start of the active turn, when known. */
|
|
116
|
+
get workingSince(): number | undefined;
|
|
117
|
+
/** @returns Sessions accounted to the selected workspace, minus archived identities. */
|
|
118
|
+
get visibleSessions(): ObjectValue[];
|
|
119
|
+
/** Load the optional preset roster once per connection. */
|
|
120
|
+
loadPresetNames(): void;
|
|
121
|
+
/** @returns Host model routes and adapter-owned reasoning choices. */
|
|
122
|
+
modelCatalog(): Promise<ObjectValue>;
|
|
123
|
+
/** Select the next request's model.
|
|
124
|
+
* @param provider - Host provider route ID.
|
|
125
|
+
* @param model - Exact model ID.
|
|
126
|
+
* @param reasoningEffort - Optional adapter-owned effort ID.
|
|
127
|
+
*/
|
|
128
|
+
selectModel(provider: string, model: string, reasoningEffort?: string): Promise<void>;
|
|
129
|
+
/** Stop the selected turn, or allow exit only while idle.
|
|
130
|
+
* @param force - Send an explicit cancellation even when the cached running flag is idle.
|
|
131
|
+
* @returns True when the caller may exit.
|
|
132
|
+
*/
|
|
133
|
+
interrupt(force?: boolean): Promise<boolean>;
|
|
134
|
+
/** Keep history stable while the user reads, searches, or expands it.
|
|
135
|
+
* @param pinned - Whether the main transcript is being read away from its tail.
|
|
136
|
+
*/
|
|
137
|
+
pinHistory(pinned: boolean): void;
|
|
138
|
+
/** Refresh all HTTP-visible sessions without changing the selected conversation.
|
|
139
|
+
* @param signal - Optional cancellation for an explicit /cost refresh.
|
|
140
|
+
*/
|
|
141
|
+
refreshCosts(signal?: AbortSignal): Promise<void>;
|
|
142
|
+
/** Refresh both lists from the host, then show the requested picker.
|
|
143
|
+
* @param screen - Picker to display after the refresh.
|
|
144
|
+
*/
|
|
145
|
+
showPicker(screen: 'workspaces' | 'sessions'): Promise<void>;
|
|
146
|
+
/** Resolve a removal command to one reviewable object.
|
|
147
|
+
* @param kind - Workspace registration removal or session archival.
|
|
148
|
+
* @param query - Exact name, ID, or unambiguous ID prefix.
|
|
149
|
+
* @returns The fixed identity and display details for confirmation.
|
|
150
|
+
*/
|
|
151
|
+
removalTarget(kind: 'workspace' | 'session', query: string): Promise<RemovalTarget>;
|
|
152
|
+
/** Apply a confirmed removal or verified empty-session archival.
|
|
153
|
+
* @param target - Exact workspace or session identity reviewed by the user.
|
|
154
|
+
*/
|
|
155
|
+
removeTarget(target: RemovalTarget): Promise<void>;
|
|
156
|
+
/** Pick a workspace, or use all sessions when the identity is omitted.
|
|
157
|
+
* @param workspaceId - Workspace to select, if any.
|
|
158
|
+
*/
|
|
159
|
+
pickWorkspace(workspaceId?: string): void;
|
|
160
|
+
/** Open a workspace picker, or resolve a workspace target.
|
|
161
|
+
* @param query - Workspace target, if any.
|
|
162
|
+
*/
|
|
163
|
+
switchWorkspace(query?: string): Promise<void>;
|
|
164
|
+
/** Guide session selection, list all sessions with `all`, or resolve a target.
|
|
165
|
+
* @param query - Session target, `all`, or nothing for the guided picker.
|
|
166
|
+
*/
|
|
167
|
+
switchSession(query?: string): Promise<void>;
|
|
168
|
+
/** Prompt for a host path without starting a local agent. */
|
|
169
|
+
enterPath(): void;
|
|
170
|
+
/** Register a host directory and move to its session picker.
|
|
171
|
+
* @param path - Absolute directory path on the host.
|
|
172
|
+
*/
|
|
173
|
+
createWorkspace(path: string): Promise<void>;
|
|
174
|
+
/** Create a session in the selected workspace. */
|
|
175
|
+
createSession(): Promise<void>;
|
|
176
|
+
/** Replace the selected transcript and follow the session.
|
|
177
|
+
* @param sessionId - Session to follow.
|
|
178
|
+
*/
|
|
179
|
+
selectSession(sessionId: string): Promise<void>;
|
|
180
|
+
/** Wait for the selected follow snapshot.
|
|
181
|
+
* @param signal - Cancels waiting without closing the session.
|
|
182
|
+
*/
|
|
183
|
+
waitForHistory(signal: AbortSignal): Promise<void>;
|
|
184
|
+
/** Search host session results.
|
|
185
|
+
* @param query - Literal message text.
|
|
186
|
+
* @param workspaceOnly - Restrict hits to the selected workspace.
|
|
187
|
+
* @param signal - Cancels the HTTP search.
|
|
188
|
+
* @returns Session snippets and the global truncation flag.
|
|
189
|
+
*/
|
|
190
|
+
searchSessions(query: string, workspaceOnly: boolean, signal: AbortSignal): Promise<{
|
|
191
|
+
items: ObjectValue[];
|
|
192
|
+
hasMore: boolean;
|
|
193
|
+
}>;
|
|
194
|
+
/** Search host paths for the composer's `@` completion.
|
|
195
|
+
* @param query - Path text after @.
|
|
196
|
+
* @param signal - Cancels an obsolete lookup.
|
|
197
|
+
* @returns Validated candidates in host order.
|
|
198
|
+
*/
|
|
199
|
+
references(query: string, signal: AbortSignal): Promise<import("../session/references.ts").FileReference[]>;
|
|
200
|
+
/** Execute a human command directly, outside the model prompt queue.
|
|
201
|
+
* @param line - Complete slash command, including arguments.
|
|
202
|
+
* @param signal - Cancels the request while the host performs compaction.
|
|
203
|
+
* @returns The host's successful command result text.
|
|
204
|
+
*/
|
|
205
|
+
command(line: string, signal: AbortSignal): Promise<string>;
|
|
206
|
+
/** Remove one host-owned pending input.
|
|
207
|
+
* @param itemId - Queue occurrence identity from session/control.
|
|
208
|
+
*/
|
|
209
|
+
removeQueued(itemId: string): Promise<void>;
|
|
210
|
+
/** Export the selected host log to a new local ZIP file.
|
|
211
|
+
* @param path - Optional local destination; existing files are never overwritten.
|
|
212
|
+
* @param signal - Cancels the download and removes an incomplete file.
|
|
213
|
+
* @returns Absolute saved filename.
|
|
214
|
+
*/
|
|
215
|
+
exportLog(path: string | undefined, signal: AbortSignal): Promise<string>;
|
|
216
|
+
/** Save loaded Markdown, diagrams and math as offline HTML.
|
|
217
|
+
* @param path - Optional filename; existing files are not replaced.
|
|
218
|
+
* @param signal - Cancels the write.
|
|
219
|
+
* @returns Absolute saved filename.
|
|
220
|
+
*/
|
|
221
|
+
exportHtml(path: string | undefined, signal: AbortSignal): Promise<string>;
|
|
222
|
+
/** Admit text once as steering while running, or a new turn while idle.
|
|
223
|
+
* @param text - Composed prompt text.
|
|
224
|
+
*/
|
|
225
|
+
prompt(text: string): Promise<void>;
|
|
226
|
+
/** Cancel the active turn; pending queue items remain host-owned. */
|
|
227
|
+
cancelTurn(): Promise<void>;
|
|
228
|
+
/** Add a page before the retained window.
|
|
229
|
+
* @param signal - Cancels local paging without interrupting the remote agent.
|
|
230
|
+
* @param transcript - Transcript to extend; defaults to the live one.
|
|
231
|
+
*/
|
|
232
|
+
older(signal?: AbortSignal, transcript?: Transcript): Promise<void>;
|
|
233
|
+
/** Search the loaded history page by page.
|
|
234
|
+
* @param query - Literal, case-insensitive text including folded reasoning.
|
|
235
|
+
* @param signal - Cancels HTTP and processing without cancelling the agent.
|
|
236
|
+
* @returns Newest-first bounded summaries and an explicit truncation flag.
|
|
237
|
+
*/
|
|
238
|
+
searchHistory(query: string, signal: AbortSignal): Promise<HistorySearch>;
|
|
239
|
+
/** Load a separate small window ending at a search target.
|
|
240
|
+
* @param target - Durable message sequence to display.
|
|
241
|
+
* @param signal - Cancels the target-page request.
|
|
242
|
+
* @returns A caller-owned historical window that must be disposed when closed.
|
|
243
|
+
*/
|
|
244
|
+
historyAt(target: number, signal: AbortSignal): Promise<Transcript>;
|
|
245
|
+
/** Load the prefix required for an explicit history jump.
|
|
246
|
+
* @param target - Visible record sequence, or first for the oldest available history.
|
|
247
|
+
* @param signal - Cancels local paging without interrupting the remote agent.
|
|
248
|
+
*/
|
|
249
|
+
historyThrough(target: number | 'first', signal: AbortSignal): Promise<void>;
|
|
250
|
+
/** Answer the oldest selected-session interaction, after explicit user action.
|
|
251
|
+
* @param value - Structured answer value or approval outcome.
|
|
252
|
+
*/
|
|
253
|
+
answer(value: Json): Promise<void>;
|
|
254
|
+
/** Approve or reject the pending approval request.
|
|
255
|
+
* @param allowed - Whether the request is approved once.
|
|
256
|
+
*/
|
|
257
|
+
approve(allowed: boolean): Promise<void>;
|
|
258
|
+
}
|
|
259
|
+
export type { HistorySearch, RemovalTarget } from '../session/types.ts';
|
|
260
|
+
export type { State } from '../state.ts';
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/** Application facade: composes the connection, session, catalog and cost domains. */
|
|
2
|
+
import { Client } from "../transport/client.js";
|
|
3
|
+
import { errorText } from "../transport/wire.js";
|
|
4
|
+
import { DEFAULT_HISTORY_LIMITS } from "../session/memory.js";
|
|
5
|
+
import { layoutStats } from "../session/history.js";
|
|
6
|
+
import { markdownCacheStats } from "../session/markdown.js";
|
|
7
|
+
import { SessionController } from "../session/controller.js";
|
|
8
|
+
import { CatalogController } from "../catalog/controller.js";
|
|
9
|
+
import { CostController } from "../cost/controller.js";
|
|
10
|
+
import { ConnectionController } from "./connection.js";
|
|
11
|
+
import { MemoryLog } from "./memory-log.js";
|
|
12
|
+
import { initialState } from "../state.js";
|
|
13
|
+
/** Application facade over the domain controllers; the UI owns only this object.
|
|
14
|
+
*
|
|
15
|
+
* State lives here, connection generations live in `connection`, the selected session and its
|
|
16
|
+
* history live in `session`, model metadata lives in `catalog`, and billing lives in `cost`.
|
|
17
|
+
*/
|
|
18
|
+
export class Controller {
|
|
19
|
+
base;
|
|
20
|
+
initialSession;
|
|
21
|
+
costs;
|
|
22
|
+
historyLimits;
|
|
23
|
+
memoryLogPath;
|
|
24
|
+
state = initialState();
|
|
25
|
+
/** Physical connection, retry loop and projection store. */
|
|
26
|
+
connection;
|
|
27
|
+
/** Selected session, follow stream, history and interactions. */
|
|
28
|
+
session;
|
|
29
|
+
/** Model routes and agent-preset metadata. */
|
|
30
|
+
catalog;
|
|
31
|
+
/** Background billing scan; present only when a ledger was supplied. */
|
|
32
|
+
cost;
|
|
33
|
+
/** Bounded runtime memory samples; present only when a log path was supplied. */
|
|
34
|
+
memoryLog;
|
|
35
|
+
observers = new Set();
|
|
36
|
+
selector = 0;
|
|
37
|
+
constructor(base, token, initialSession, makeClient = () => new Client(base), authenticate = client => client.authenticate(token ?? ''), costs, historyLimits = DEFAULT_HISTORY_LIMITS, memoryLogPath) {
|
|
38
|
+
this.base = base;
|
|
39
|
+
this.initialSession = initialSession;
|
|
40
|
+
this.costs = costs;
|
|
41
|
+
this.historyLimits = historyLimits;
|
|
42
|
+
this.memoryLogPath = memoryLogPath;
|
|
43
|
+
const options = { base, token, initialSession, makeClient, authenticate };
|
|
44
|
+
this.connection = new ConnectionController(this, options, this);
|
|
45
|
+
this.session = new SessionController(this, this.connection, this.connection, historyLimits);
|
|
46
|
+
this.catalog = new CatalogController(this, this.connection);
|
|
47
|
+
if (costs)
|
|
48
|
+
this.cost = new CostController(costs, {
|
|
49
|
+
client: () => this.connection.client(),
|
|
50
|
+
online: () => this.state.online,
|
|
51
|
+
signal: () => this.connection.signal(),
|
|
52
|
+
publish: () => this.update({}),
|
|
53
|
+
});
|
|
54
|
+
if (memoryLogPath !== undefined)
|
|
55
|
+
this.memoryLog = new MemoryLog(memoryLogPath, () => this.memorySample());
|
|
56
|
+
}
|
|
57
|
+
/** React-compatible state subscription. */
|
|
58
|
+
subscribe = (listener) => {
|
|
59
|
+
this.observers.add(listener);
|
|
60
|
+
return () => this.observers.delete(listener);
|
|
61
|
+
};
|
|
62
|
+
/** Snapshot identity changes only when the controller publishes. */
|
|
63
|
+
snapshot = () => this.state;
|
|
64
|
+
/** Current projection store; replaced at each connection generation. */
|
|
65
|
+
get telemetry() { return this.connection.telemetry; }
|
|
66
|
+
/** @returns The current selector generation. */
|
|
67
|
+
selection() { return this.selector; }
|
|
68
|
+
/** Advance the selector generation when the selected workspace or session changes. */
|
|
69
|
+
bumpSelection() { this.selector++; }
|
|
70
|
+
/** Publish a state patch, re-deriving the visible pending interactions.
|
|
71
|
+
* @param patch - Fields to replace on the current state.
|
|
72
|
+
*/
|
|
73
|
+
update(patch) {
|
|
74
|
+
const next = { ...this.state, ...patch, version: this.state.version + 1 };
|
|
75
|
+
next.pending = next.online && next.screen === 'chat' && this.session
|
|
76
|
+
? this.session.pendingFor(next) : [];
|
|
77
|
+
this.state = next;
|
|
78
|
+
for (const observer of this.observers)
|
|
79
|
+
observer();
|
|
80
|
+
}
|
|
81
|
+
/** Start one retry loop, with a fresh snapshot generation after every disconnect. */
|
|
82
|
+
start() { this.connection.start(); this.memoryLog?.start(); }
|
|
83
|
+
/** Cancel retries and HTTP, close the socket, and release session and catalog work. */
|
|
84
|
+
async stop() {
|
|
85
|
+
await this.connection.stop();
|
|
86
|
+
await this.session.settle();
|
|
87
|
+
await this.catalog.settle();
|
|
88
|
+
await this.cost?.stop();
|
|
89
|
+
await this.memoryLog?.stop();
|
|
90
|
+
this.session.release();
|
|
91
|
+
}
|
|
92
|
+
/** Stop the selected turn and then close, so quitting does not leave host work running.
|
|
93
|
+
* An idle session stays untouched, and an in-flight cancellation is awaited rather than repeated.
|
|
94
|
+
*/
|
|
95
|
+
async shutdown() {
|
|
96
|
+
if (this.session.active)
|
|
97
|
+
await this.session.interrupt(true);
|
|
98
|
+
await this.stop();
|
|
99
|
+
}
|
|
100
|
+
/** Run a UI operation and expose errors without destroying the current input.
|
|
101
|
+
* @param operation - Operation to run while the client is busy.
|
|
102
|
+
* @returns Whether the operation completed.
|
|
103
|
+
*/
|
|
104
|
+
async perform(operation) {
|
|
105
|
+
if (this.state.busy || !this.state.online)
|
|
106
|
+
return false;
|
|
107
|
+
this.update({ busy: true, error: '' });
|
|
108
|
+
try {
|
|
109
|
+
await operation();
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
this.update({ error: errorText(error) });
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
this.update({ busy: false });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Read the counters one memory sample records; content never leaves as text.
|
|
121
|
+
*
|
|
122
|
+
* The retained transcript and the ledger are small in practice, so a sample also reads the two
|
|
123
|
+
* structures that grow with rendered content — the layout row cache and the bounded math and
|
|
124
|
+
* diagram cache — and the work the last cost scan re-read, which is the only timer here whose
|
|
125
|
+
* per-pass work scales with history. With a runtime that exposes `gc`, the sample also reports
|
|
126
|
+
* the heap after a forced collection, so retained state and uncollected garbage stay distinct.
|
|
127
|
+
*/
|
|
128
|
+
memorySample() {
|
|
129
|
+
const memory = process.memoryUsage();
|
|
130
|
+
const transcript = this.state.transcript;
|
|
131
|
+
const ledger = this.costs?.summary();
|
|
132
|
+
const layout = layoutStats(transcript);
|
|
133
|
+
const markdown = markdownCacheStats();
|
|
134
|
+
const gc = this.forcedGc();
|
|
135
|
+
return {
|
|
136
|
+
time: new Date().toISOString(),
|
|
137
|
+
rss: memory.rss, heapTotal: memory.heapTotal, heapUsed: memory.heapUsed,
|
|
138
|
+
external: memory.external, arrayBuffers: memory.arrayBuffers,
|
|
139
|
+
...(gc === undefined ? {} : { heapUsedAfterGc: gc.used, gcMs: gc.ms }),
|
|
140
|
+
online: this.state.online, screen: this.state.screen,
|
|
141
|
+
session: this.state.sessionId ?? null,
|
|
142
|
+
pinned: this.session.pinned,
|
|
143
|
+
records: transcript.retainedRecordCount, retainedBytes: transcript.retainedBytes,
|
|
144
|
+
beforeSeq: transcript.beforeSeq ?? null, hasMore: transcript.hasMore,
|
|
145
|
+
live: transcript.hasLiveContent, liveChars: transcript.liveText.length, pending: this.state.pending.length,
|
|
146
|
+
thoughts: transcript.thoughts.length,
|
|
147
|
+
...(layout === undefined ? {} : { layoutRows: layout.rows, layoutCacheBytes: layout.cacheBytes, layoutSpans: layout.spans,
|
|
148
|
+
layoutSpanChars: layout.spanChars, layoutLiveWraps: layout.liveWraps, layoutLiveMarkdown: layout.liveMarkdown }),
|
|
149
|
+
markdownEntries: markdown.entries, markdownChars: markdown.chars, markdownHits: markdown.hits, markdownMisses: markdown.misses,
|
|
150
|
+
scanning: this.costs?.scanning ?? false,
|
|
151
|
+
...(this.costs?.lastScan === undefined ? {} : { scanSessions: this.costs.lastScan.sessions,
|
|
152
|
+
scanPages: this.costs.lastScan.pages, scanEvents: this.costs.lastScan.events }),
|
|
153
|
+
...(ledger === undefined ? {} : { ledgerSessions: ledger.sessions, ledgerCharges: ledger.charges, ledgerUnpriced: ledger.unpriced }),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/** Collect before reading the heap when the runtime exposes a collection.
|
|
157
|
+
* @returns Heap in use after the collection and how long it took, or undefined without `global.gc`.
|
|
158
|
+
*/
|
|
159
|
+
forcedGc() {
|
|
160
|
+
const collect = globalThis.gc;
|
|
161
|
+
if (typeof collect !== 'function')
|
|
162
|
+
return undefined;
|
|
163
|
+
const start = Date.now();
|
|
164
|
+
collect();
|
|
165
|
+
return { used: process.memoryUsage().heapUsed, ms: Date.now() - start };
|
|
166
|
+
}
|
|
167
|
+
/** A new generation starts; drop generation-scoped domain state. */
|
|
168
|
+
begin() {
|
|
169
|
+
this.session.beginGeneration();
|
|
170
|
+
this.catalog.reset();
|
|
171
|
+
this.update({ controlError: undefined });
|
|
172
|
+
}
|
|
173
|
+
/** The event stream is ready and the control baseline is applied. */
|
|
174
|
+
async ready() {
|
|
175
|
+
this.catalog.refresh();
|
|
176
|
+
this.update({ online: true, status: 'Connected', error: '', pending: [] });
|
|
177
|
+
const screen = this.state.screen;
|
|
178
|
+
await this.session.showPicker(screen === 'sessions' ? 'sessions' : 'workspaces');
|
|
179
|
+
const sessionId = this.state.sessionId ?? this.initialSession;
|
|
180
|
+
if (sessionId && (screen === 'chat' || this.initialSession && !this.state.sessionId))
|
|
181
|
+
await this.session.selectSession(sessionId);
|
|
182
|
+
this.cost?.start();
|
|
183
|
+
}
|
|
184
|
+
/** The generation ended; invalidate session work and stop the scan. */
|
|
185
|
+
async ended() {
|
|
186
|
+
this.session.endGeneration();
|
|
187
|
+
await this.cost?.stop();
|
|
188
|
+
}
|
|
189
|
+
/** Deliver a host waterfall to the session domain.
|
|
190
|
+
* @param frame - One decoded waterfall frame.
|
|
191
|
+
* @returns Whether the session domain retained it.
|
|
192
|
+
*/
|
|
193
|
+
waterfall(frame) { return this.session.waterfall(frame); }
|
|
194
|
+
/** Drop a waterfall the host cancelled.
|
|
195
|
+
* @param eventId - Correlation id previously retained.
|
|
196
|
+
*/
|
|
197
|
+
cancelled(eventId) { this.session.cancelled(eventId); }
|
|
198
|
+
/** Apply one host running-state notification.
|
|
199
|
+
* @param sessionId - Session whose state changed.
|
|
200
|
+
* @param running - Whether the host still runs that session.
|
|
201
|
+
*/
|
|
202
|
+
status(sessionId, running) { this.session.status(sessionId, running); }
|
|
203
|
+
/** Surface a host-reported session error.
|
|
204
|
+
* @param sessionId - Session the host reported on.
|
|
205
|
+
* @param error - Error payload as delivered by the host.
|
|
206
|
+
*/
|
|
207
|
+
error(sessionId, error) { this.session.reportError(sessionId, error); }
|
|
208
|
+
/** Reload the model catalog after a host settings, credential or adapter change. */
|
|
209
|
+
invalidated() { this.catalog.refresh(); }
|
|
210
|
+
/** Refresh billing after a turn finished. */
|
|
211
|
+
idle() { this.cost?.onTurnIdle(); }
|
|
212
|
+
/** @returns Host running state of the selected session. */
|
|
213
|
+
get running() { return this.session.running; }
|
|
214
|
+
/** @returns Current session title, falling back to the list title and then the ID. */
|
|
215
|
+
get sessionName() { return this.session.sessionName; }
|
|
216
|
+
/** @returns Current agent-preset label. */
|
|
217
|
+
get sessionMode() { return this.session.sessionMode; }
|
|
218
|
+
/** @returns Epoch start of the active turn, when known. */
|
|
219
|
+
get workingSince() { return this.session.workingSince; }
|
|
220
|
+
/** @returns Sessions accounted to the selected workspace, minus archived identities. */
|
|
221
|
+
get visibleSessions() { return this.session.visibleSessions; }
|
|
222
|
+
/** Load the optional preset roster once per connection. */
|
|
223
|
+
loadPresetNames() { this.catalog.loadPresetNames(); }
|
|
224
|
+
/** @returns Host model routes and adapter-owned reasoning choices. */
|
|
225
|
+
async modelCatalog() { return this.catalog.modelCatalog(); }
|
|
226
|
+
/** Select the next request's model.
|
|
227
|
+
* @param provider - Host provider route ID.
|
|
228
|
+
* @param model - Exact model ID.
|
|
229
|
+
* @param reasoningEffort - Optional adapter-owned effort ID.
|
|
230
|
+
*/
|
|
231
|
+
async selectModel(provider, model, reasoningEffort) {
|
|
232
|
+
await this.catalog.selectModel(provider, model, reasoningEffort);
|
|
233
|
+
}
|
|
234
|
+
/** Stop the selected turn, or allow exit only while idle.
|
|
235
|
+
* @param force - Send an explicit cancellation even when the cached running flag is idle.
|
|
236
|
+
* @returns True when the caller may exit.
|
|
237
|
+
*/
|
|
238
|
+
interrupt(force = false) { return this.session.interrupt(force); }
|
|
239
|
+
/** Keep history stable while the user reads, searches, or expands it.
|
|
240
|
+
* @param pinned - Whether the main transcript is being read away from its tail.
|
|
241
|
+
*/
|
|
242
|
+
pinHistory(pinned) { this.session.pinHistory(pinned); }
|
|
243
|
+
/** Refresh all HTTP-visible sessions without changing the selected conversation.
|
|
244
|
+
* @param signal - Optional cancellation for an explicit /cost refresh.
|
|
245
|
+
*/
|
|
246
|
+
async refreshCosts(signal = this.connection.signal()) { await this.cost?.refresh(signal); }
|
|
247
|
+
/** Refresh both lists from the host, then show the requested picker.
|
|
248
|
+
* @param screen - Picker to display after the refresh.
|
|
249
|
+
*/
|
|
250
|
+
async showPicker(screen) { await this.session.showPicker(screen); }
|
|
251
|
+
/** Resolve a removal command to one reviewable object.
|
|
252
|
+
* @param kind - Workspace registration removal or session archival.
|
|
253
|
+
* @param query - Exact name, ID, or unambiguous ID prefix.
|
|
254
|
+
* @returns The fixed identity and display details for confirmation.
|
|
255
|
+
*/
|
|
256
|
+
async removalTarget(kind, query) { return this.session.removalTarget(kind, query); }
|
|
257
|
+
/** Apply a confirmed removal or verified empty-session archival.
|
|
258
|
+
* @param target - Exact workspace or session identity reviewed by the user.
|
|
259
|
+
*/
|
|
260
|
+
async removeTarget(target) { await this.session.removeTarget(target); }
|
|
261
|
+
/** Pick a workspace, or use all sessions when the identity is omitted.
|
|
262
|
+
* @param workspaceId - Workspace to select, if any.
|
|
263
|
+
*/
|
|
264
|
+
pickWorkspace(workspaceId) { this.session.pickWorkspace(workspaceId); }
|
|
265
|
+
/** Open a workspace picker, or resolve a workspace target.
|
|
266
|
+
* @param query - Workspace target, if any.
|
|
267
|
+
*/
|
|
268
|
+
async switchWorkspace(query) { await this.session.switchWorkspace(query); }
|
|
269
|
+
/** Guide session selection, list all sessions with `all`, or resolve a target.
|
|
270
|
+
* @param query - Session target, `all`, or nothing for the guided picker.
|
|
271
|
+
*/
|
|
272
|
+
async switchSession(query) { await this.session.switchSession(query); }
|
|
273
|
+
/** Prompt for a host path without starting a local agent. */
|
|
274
|
+
enterPath() { this.session.enterPath(); }
|
|
275
|
+
/** Register a host directory and move to its session picker.
|
|
276
|
+
* @param path - Absolute directory path on the host.
|
|
277
|
+
*/
|
|
278
|
+
async createWorkspace(path) { await this.session.createWorkspace(path); }
|
|
279
|
+
/** Create a session in the selected workspace. */
|
|
280
|
+
async createSession() { await this.session.createSession(); }
|
|
281
|
+
/** Replace the selected transcript and follow the session.
|
|
282
|
+
* @param sessionId - Session to follow.
|
|
283
|
+
*/
|
|
284
|
+
async selectSession(sessionId) { await this.session.selectSession(sessionId); }
|
|
285
|
+
/** Wait for the selected follow snapshot.
|
|
286
|
+
* @param signal - Cancels waiting without closing the session.
|
|
287
|
+
*/
|
|
288
|
+
async waitForHistory(signal) { await this.session.waitForHistory(signal); }
|
|
289
|
+
/** Search host session results.
|
|
290
|
+
* @param query - Literal message text.
|
|
291
|
+
* @param workspaceOnly - Restrict hits to the selected workspace.
|
|
292
|
+
* @param signal - Cancels the HTTP search.
|
|
293
|
+
* @returns Session snippets and the global truncation flag.
|
|
294
|
+
*/
|
|
295
|
+
async searchSessions(query, workspaceOnly, signal) {
|
|
296
|
+
return this.session.searchSessions(query, workspaceOnly, signal);
|
|
297
|
+
}
|
|
298
|
+
/** Search host paths for the composer's `@` completion.
|
|
299
|
+
* @param query - Path text after @.
|
|
300
|
+
* @param signal - Cancels an obsolete lookup.
|
|
301
|
+
* @returns Validated candidates in host order.
|
|
302
|
+
*/
|
|
303
|
+
async references(query, signal) { return this.session.references(query, signal); }
|
|
304
|
+
/** Execute a human command directly, outside the model prompt queue.
|
|
305
|
+
* @param line - Complete slash command, including arguments.
|
|
306
|
+
* @param signal - Cancels the request while the host performs compaction.
|
|
307
|
+
* @returns The host's successful command result text.
|
|
308
|
+
*/
|
|
309
|
+
async command(line, signal) { return this.session.command(line, signal); }
|
|
310
|
+
/** Remove one host-owned pending input.
|
|
311
|
+
* @param itemId - Queue occurrence identity from session/control.
|
|
312
|
+
*/
|
|
313
|
+
async removeQueued(itemId) { await this.session.removeQueued(itemId); }
|
|
314
|
+
/** Export the selected host log to a new local ZIP file.
|
|
315
|
+
* @param path - Optional local destination; existing files are never overwritten.
|
|
316
|
+
* @param signal - Cancels the download and removes an incomplete file.
|
|
317
|
+
* @returns Absolute saved filename.
|
|
318
|
+
*/
|
|
319
|
+
async exportLog(path, signal) { return this.session.exportLog(path, signal); }
|
|
320
|
+
/** Save loaded Markdown, diagrams and math as offline HTML.
|
|
321
|
+
* @param path - Optional filename; existing files are not replaced.
|
|
322
|
+
* @param signal - Cancels the write.
|
|
323
|
+
* @returns Absolute saved filename.
|
|
324
|
+
*/
|
|
325
|
+
async exportHtml(path, signal) { return this.session.exportHtml(path, signal); }
|
|
326
|
+
/** Admit text once as steering while running, or a new turn while idle.
|
|
327
|
+
* @param text - Composed prompt text.
|
|
328
|
+
*/
|
|
329
|
+
async prompt(text) { await this.session.prompt(text); }
|
|
330
|
+
/** Cancel the active turn; pending queue items remain host-owned. */
|
|
331
|
+
async cancelTurn() { await this.session.cancelTurn(); }
|
|
332
|
+
/** Add a page before the retained window.
|
|
333
|
+
* @param signal - Cancels local paging without interrupting the remote agent.
|
|
334
|
+
* @param transcript - Transcript to extend; defaults to the live one.
|
|
335
|
+
*/
|
|
336
|
+
async older(signal, transcript) { await this.session.older(signal, transcript); }
|
|
337
|
+
/** Search the loaded history page by page.
|
|
338
|
+
* @param query - Literal, case-insensitive text including folded reasoning.
|
|
339
|
+
* @param signal - Cancels HTTP and processing without cancelling the agent.
|
|
340
|
+
* @returns Newest-first bounded summaries and an explicit truncation flag.
|
|
341
|
+
*/
|
|
342
|
+
async searchHistory(query, signal) { return this.session.searchHistory(query, signal); }
|
|
343
|
+
/** Load a separate small window ending at a search target.
|
|
344
|
+
* @param target - Durable message sequence to display.
|
|
345
|
+
* @param signal - Cancels the target-page request.
|
|
346
|
+
* @returns A caller-owned historical window that must be disposed when closed.
|
|
347
|
+
*/
|
|
348
|
+
async historyAt(target, signal) { return this.session.historyAt(target, signal); }
|
|
349
|
+
/** Load the prefix required for an explicit history jump.
|
|
350
|
+
* @param target - Visible record sequence, or first for the oldest available history.
|
|
351
|
+
* @param signal - Cancels local paging without interrupting the remote agent.
|
|
352
|
+
*/
|
|
353
|
+
async historyThrough(target, signal) { await this.session.historyThrough(target, signal); }
|
|
354
|
+
/** Answer the oldest selected-session interaction, after explicit user action.
|
|
355
|
+
* @param value - Structured answer value or approval outcome.
|
|
356
|
+
*/
|
|
357
|
+
async answer(value) { await this.session.answer(value); }
|
|
358
|
+
/** Approve or reject the pending approval request.
|
|
359
|
+
* @param allowed - Whether the request is approved once.
|
|
360
|
+
*/
|
|
361
|
+
async approve(allowed) { await this.session.approve(allowed); }
|
|
362
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Controller domain: the application facade and the connection it owns. */
|
|
2
|
+
export { Controller } from './controller.ts';
|
|
3
|
+
export type { HistorySearch, RemovalTarget, State } from './controller.ts';
|
|
4
|
+
export { ConnectionController } from './connection.ts';
|
|
5
|
+
export type { ConnectionListener, ConnectionOptions } from './connection.ts';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ObjectValue } from '../transport/wire.ts';
|
|
2
|
+
/** Append-only memory log; the file never exceeds twice `KEEP_SAMPLES` lines.
|
|
3
|
+
*
|
|
4
|
+
* The log exists to answer "is retained content growing, or is this just V8's high-water mark",
|
|
5
|
+
* so each sample records the process counters next to the client's retained window and the
|
|
6
|
+
* controller state that decides whether that window is being reclaimed. Samples contain counts
|
|
7
|
+
* and sizes only, never prompt, tool, or session text. A failing log stops itself instead of
|
|
8
|
+
* breaking the client.
|
|
9
|
+
*/
|
|
10
|
+
export declare class MemoryLog {
|
|
11
|
+
readonly path: string | undefined;
|
|
12
|
+
private readonly sampleSource;
|
|
13
|
+
private timer;
|
|
14
|
+
private appended;
|
|
15
|
+
private writing;
|
|
16
|
+
/** Last write failure, when the log stopped itself. */
|
|
17
|
+
error: string | undefined;
|
|
18
|
+
constructor(path: string | undefined, sampleSource: () => ObjectValue);
|
|
19
|
+
/** Write the header into a new file, then sample once; a missing or unwritable path is reported
|
|
20
|
+
* by that first sample instead of here.
|
|
21
|
+
*/
|
|
22
|
+
private begin;
|
|
23
|
+
/** Sample once now and then every `SAMPLE_INTERVAL_MS`; absent when no path was configured. */
|
|
24
|
+
start(): void;
|
|
25
|
+
/** Stop sampling and rewrite the file with its retained window. */
|
|
26
|
+
stop(): Promise<void>;
|
|
27
|
+
/** Append one sample line, skipping overlapping calls.
|
|
28
|
+
*
|
|
29
|
+
* A repeated failure would be noise, so the first one stops the timer and leaves the client
|
|
30
|
+
* running; the log is diagnostics, not a feature the terminal depends on.
|
|
31
|
+
*/
|
|
32
|
+
sample(): Promise<void>;
|
|
33
|
+
/** Rewrite the file with the header and the newest kept lines, bounding its size. */
|
|
34
|
+
private compact;
|
|
35
|
+
}
|