opencode-usage-insights 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1,108 @@
1
+ import { Database } from "bun:sqlite";
2
+ import type { EndpointInfo } from "../endpoint";
3
+ /**
4
+ * Persistent usage store. One SQLite database holds every recorded step,
5
+ * prompt, event and session row — and nothing else: the recorder only writes
6
+ * what a view reads.
7
+ *
8
+ * The database is opened in WAL mode with a busy timeout so the server
9
+ * recorder process and the TUI reader process can share it safely. The two
10
+ * sides open it differently on purpose:
11
+ *
12
+ * - `openStatsDb` writes: schema, migrations and recording (the server).
13
+ * - `openStatsDbReadOnly` only reads (the TUI), so no view can change the file
14
+ * and no reader pays for a schema write.
15
+ * - `openStatsDbWrite` is a short-lived maintenance connection (prune, vacuum)
16
+ * that gives up quickly instead of holding the recorder up.
17
+ */
18
+ export declare const SCHEMA_VERSION = 8;
19
+ /** Tables carried by `exportNdjson` / `importNdjson`, in import-safe order. */
20
+ export declare const TABLES: readonly ["sessions", "prompts", "events", "steps"];
21
+ export type TableName = (typeof TABLES)[number];
22
+ export type StepRecord = {
23
+ messageID: string;
24
+ sessionID: string;
25
+ parentID?: string | undefined;
26
+ providerID?: string | undefined;
27
+ modelID?: string | undefined;
28
+ variant?: string | undefined;
29
+ input?: number | undefined;
30
+ output?: number | undefined;
31
+ reasoning?: number | undefined;
32
+ cacheRead?: number | undefined;
33
+ cacheWrite?: number | undefined;
34
+ cost?: number | undefined;
35
+ createdAt?: number | undefined;
36
+ streamedAt?: number | undefined;
37
+ completedAt?: number | undefined;
38
+ finish?: string | undefined;
39
+ /** Gateway routing endpoint that served the request (`orcarouter-dsv4.1flash`). */
40
+ router?: string | undefined;
41
+ /** Clock of the recording itself, defaults to `Date.now()`. */
42
+ recordedAt?: number | undefined;
43
+ };
44
+ export type PromptRecord = {
45
+ sessionID: string;
46
+ messageID: string;
47
+ createdAt?: number | undefined;
48
+ };
49
+ export type EventRecord = {
50
+ sessionID: string;
51
+ kind: string;
52
+ messageID?: string | undefined;
53
+ at?: number | undefined;
54
+ data?: unknown;
55
+ };
56
+ export type SessionRecord = {
57
+ sessionID: string;
58
+ parentID?: string | undefined;
59
+ directory?: string | undefined;
60
+ title?: string | undefined;
61
+ startedAt?: number | undefined;
62
+ updatedAt?: number | undefined;
63
+ };
64
+ /** Open (creating when needed) the stats database and apply the schema. */
65
+ export declare function openStatsDb(path: string): Database;
66
+ /**
67
+ * Reader connection for the TUI: no schema, no migration, no write. It throws
68
+ * when the database is absent or was never written by the recorder, so the
69
+ * caller can say so instead of showing an empty panel.
70
+ */
71
+ export declare function openStatsDbReadOnly(path: string): Database;
72
+ /**
73
+ * Short-lived write connection for maintenance. The busy timeout is short on
74
+ * purpose: a prune or a vacuum that cannot take the lock must fail in front of
75
+ * the user, not hold the recorder's response path for seconds.
76
+ */
77
+ export declare function openStatsDbWrite(path: string): Database;
78
+ /**
79
+ * Insert or refresh an assistant step. Idempotent by message ID so replayed
80
+ * events, restarts and NDJSON imports never duplicate usage. Non-null fields
81
+ * discovered later (the routing endpoint) survive a re-record with `NULL`
82
+ * through COALESCE.
83
+ */
84
+ export declare function recordStep(db: Database, record: StepRecord): void;
85
+ export declare function recordPrompt(db: Database, record: PromptRecord): void;
86
+ export declare function recordEvent(db: Database, record: EventRecord): void;
87
+ export declare function upsertSession(db: Database, record: SessionRecord): void;
88
+ /**
89
+ * Attach the endpoint discovered after the step was recorded. A step that
90
+ * already carries the same endpoint is left alone: the recorder runs on the
91
+ * request path, and a no-op write there is a cost with no reader.
92
+ */
93
+ export declare function setStepRouter(db: Database, messageID: string, info: EndpointInfo): void;
94
+ /**
95
+ * Apply the endpoint learned from a compaction to every step of that session
96
+ * and model that still lacks one. Returns the number of rows touched.
97
+ */
98
+ export declare function setStepRouterForSessionModel(db: Database, sessionID: string, modelID: string | undefined, info: EndpointInfo): number;
99
+ /** Dump every table as NDJSON (`{"table": "...", "row": {...}}` per line). */
100
+ export declare function exportNdjson(db: Database): string;
101
+ /**
102
+ * Load an `exportNdjson` dump back in. Rows are inserted with `OR IGNORE`, so
103
+ * importing the same dump twice is harmless. Unknown tables and rows that are
104
+ * not objects are rejected; a row that cannot fill a required column is
105
+ * skipped rather than aborting the rest of the import. A column the row does
106
+ * not carry is left to its default, the same way a hand-written insert is.
107
+ */
108
+ export declare function importNdjson(db: Database, text: string): number;
@@ -0,0 +1,49 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import { type TableName } from "./db";
3
+ /**
4
+ * Database maintenance: what the file holds, how much of it is worth keeping,
5
+ * and the two operations that give the space back.
6
+ *
7
+ * Everything is explicit — nothing here runs on its own. The panel's `db` view
8
+ * and `bun run report` are the only callers.
9
+ */
10
+ export type TableSize = {
11
+ name: string;
12
+ rows: number;
13
+ };
14
+ export type DbInfo = {
15
+ path: string;
16
+ /** Size of the database file, in bytes. */
17
+ bytes: number;
18
+ /** Size of its write-ahead log, when one exists. */
19
+ walBytes: number | undefined;
20
+ schemaVersion: number;
21
+ tables: TableSize[];
22
+ /** Oldest and newest recorded step, the window the views can answer for. */
23
+ firstAt: number | undefined;
24
+ lastAt: number | undefined;
25
+ /** Steps a prune at each cutoff would remove, for the panel to offer choices. */
26
+ removable: Array<{
27
+ days: number;
28
+ steps: number;
29
+ }>;
30
+ };
31
+ export declare function dbInfo(db: Database, path: string, now?: number): DbInfo;
32
+ export type PruneResult = {
33
+ steps: number;
34
+ prompts: number;
35
+ events: number;
36
+ };
37
+ /**
38
+ * Delete recorded usage older than `cutoff`. Steps, their prompts and the
39
+ * compaction events that explain their cache losses go together: a compaction
40
+ * that no step references answers nothing.
41
+ */
42
+ export declare function pruneOlderThan(db: Database, cutoff: number): PruneResult;
43
+ /** Reclaim the space a prune freed. Returns the bytes the database lost. */
44
+ export declare function vacuum(db: Database, path: string): {
45
+ before: number;
46
+ after: number;
47
+ freed: number;
48
+ };
49
+ export type { TableName };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * SQLite database used when no `db` option is configured: the per-user state
3
+ * directory of the current OS, never a shared temp directory.
4
+ *
5
+ * The subdirectory is the plugin's package name, never a generic one: the
6
+ * `opencode` tree is shared with OpenCode itself and with every other plugin,
7
+ * so a `stats/` of our own would be a collision waiting to happen.
8
+ *
9
+ * - Windows: `%LOCALAPPDATA%\opencode\opencode-usage-insights\usage.db`
10
+ * - macOS: `~/Library/Application Support/opencode/opencode-usage-insights/usage.db`
11
+ * - Linux/other: `$XDG_STATE_HOME/opencode/opencode-usage-insights/usage.db`,
12
+ * falling back to `~/.local/state/opencode/opencode-usage-insights/usage.db`
13
+ *
14
+ * Pure by design so every OS can be asserted from any machine.
15
+ */
16
+ export declare function defaultDbPath(env?: Record<string, string | undefined>, platform?: string): string;
17
+ /**
18
+ * Expand a leading `~` (or `~/`), so a configured path reads the way every
19
+ * shell does. Windows accepts the backslash form too.
20
+ */
21
+ export declare function expandHome(path: string, home?: string): string;
22
+ /**
23
+ * Resolve the effective database path: explicit plugin option first, then the
24
+ * `OPENCODE_USAGE_INSIGHTS_DB` escape hatch (handy for dev and tests), then
25
+ * the per-OS state directory. A leading `~` is expanded in all three.
26
+ */
27
+ export declare function resolveDbPath(configured?: string | undefined, env?: Record<string, string | undefined>, platform?: string): string;
28
+ /**
29
+ * A configured path must not be a directory, a device or a FIFO: SQLite would
30
+ * fail obscurely, or write where nobody expects it. Symlinks to a regular file
31
+ * are followed (`statSync`, not `lstat`) because moving the state directory to
32
+ * another disk is a legitimate setup. A missing file is fine — the plugin
33
+ * creates it.
34
+ */
35
+ export declare function ensureRegularFile(path: string): void;
@@ -0,0 +1,259 @@
1
+ import type { Database } from "bun:sqlite";
2
+ /**
3
+ * Read side of the stats engine: every function takes an open database and
4
+ * returns plain rows for the TUI panel, the report script and the NDJSON
5
+ * exports. Aggregations stay in SQL; comparisons and percentiles that would
6
+ * need window functions or custom SQLite builds are computed in JS.
7
+ */
8
+ /** Event kind written by the recorder when a compaction completes. */
9
+ export declare const COMPACTION_KIND = "compaction";
10
+ /**
11
+ * Idle gap above which a cache reuse loss is attributed to provider-side
12
+ * expiry rather than to a mid-session event. Heuristic: the empirical TTL
13
+ * evidence view narrows this down with real observations.
14
+ */
15
+ export declare const DEFAULT_TTL_HINT_MS: number;
16
+ export type Range = {
17
+ from?: number | undefined;
18
+ to?: number | undefined;
19
+ };
20
+ /**
21
+ * Panel filters. They narrow every aggregation to one model, one endpoint or
22
+ * one session, and are applied with the same `WHERE` builder the range uses so
23
+ * a filtered view and a drill-down agree.
24
+ */
25
+ export type Filters = {
26
+ /** Model id, variant excluded. */
27
+ modelID?: string | undefined;
28
+ /**
29
+ * One model *and* its variant, as `modelKey` builds it (`m1#max`). A variant
30
+ * is a different model to the provider, so a drill-down into one variant must
31
+ * not pick up its family.
32
+ */
33
+ modelKey?: string | undefined;
34
+ /**
35
+ * The routing endpoint that served the request (`router`). It is the one
36
+ * identity that answers "who ran this inference": the provider you configured
37
+ * says where the request came from, which is evidence, not an answer.
38
+ */
39
+ router?: string | undefined;
40
+ /**
41
+ * One session, exactly. Used by the `session` range and by the overview's
42
+ * "this session" block, which must not pick up the steps of every other
43
+ * session that happened to run after it started.
44
+ */
45
+ sessionID?: string | undefined;
46
+ };
47
+ export type Usage = {
48
+ steps: number;
49
+ input: number;
50
+ cached: number;
51
+ cacheWrite: number;
52
+ output: number;
53
+ reasoning: number;
54
+ cost: number;
55
+ };
56
+ export declare const STEP_TIME = "COALESCE(created_at, recorded_at)";
57
+ /**
58
+ * The identity of a model *and* its variant, in SQL: `modelKey` says a variant
59
+ * is a different model to the provider, so a variant never shares a row with
60
+ * its family. The JS twin is `modelKey` below; both have to agree.
61
+ */
62
+ export declare const MODEL_KEY_SQL = "COALESCE(model_id, '(unknown)') || '#' || COALESCE(variant, '')";
63
+ /** `modelKey(modelID, variant)`: the family, with the variant as a suffix. */
64
+ export declare function modelKey(modelID: string, variant: string | undefined): string;
65
+ /** Human label of a model key or a model id and variant. */
66
+ export declare function modelLabel(modelID: string, variant: string | undefined): string;
67
+ /**
68
+ * `WHERE` for a range plus the panel filters. Shared with the analysis layer so
69
+ * a filtered view and a drill-down agree on what is in scope.
70
+ */
71
+ export declare function buildWhere(range: Range | undefined, filters?: Filters | undefined, column?: string): {
72
+ where: string;
73
+ params: (string | number)[];
74
+ };
75
+ /**
76
+ * Share of the whole prompt that came from the cache. The denominator is what
77
+ * the provider saw: cache reads, fresh input and cache writes.
78
+ */
79
+ export declare function cacheHitRatio(usage: Pick<Usage, "input" | "cached" | "cacheWrite">): number | undefined;
80
+ export type Overview = Usage & {
81
+ sessions: number;
82
+ prompts: number;
83
+ firstAt: number | undefined;
84
+ lastAt: number | undefined;
85
+ cacheHitRatio: number | undefined;
86
+ /**
87
+ * Tokens generated per second of streaming, over the whole selection: the
88
+ * produced tokens divided by the summed streaming windows. A weighted average,
89
+ * not the mean of the per-step rates the latency view reports as p50.
90
+ */
91
+ tps: number | undefined;
92
+ };
93
+ export declare function overview(db: Database, range?: Range, filters?: Filters): Overview;
94
+ export type ProviderUsage = Usage & {
95
+ providerID: string;
96
+ /** Gateway routing endpoints that served the requests. */
97
+ routers: string[];
98
+ firstAt: number | undefined;
99
+ lastAt: number | undefined;
100
+ };
101
+ export type ModelUsage = {
102
+ modelID: string;
103
+ variant: string | undefined;
104
+ providers: ProviderUsage[];
105
+ totals: Usage;
106
+ };
107
+ /**
108
+ * Usage per model, each with the providers that served it. This is the view
109
+ * that answers "which inference providers is this model actually coming from
110
+ * over the selected period".
111
+ */
112
+ export declare function models(db: Database, range?: Range, filters?: Filters): ModelUsage[];
113
+ export type TimelineBucket = {
114
+ at: number;
115
+ } & Usage;
116
+ /** Usage aggregated into fixed time buckets (for example one hour or day). */
117
+ export declare function timeline(db: Database, range: Range | undefined, bucketMs: number): TimelineBucket[];
118
+ export type DropReason = "compaction" | "expired" | "unknown";
119
+ export type CacheDropRow = {
120
+ sessionID: string;
121
+ messageID: string;
122
+ providerID: string | undefined;
123
+ modelID: string | undefined;
124
+ variant: string | undefined;
125
+ at: number;
126
+ tokens: number;
127
+ gapMs: number | undefined;
128
+ prevCached: number;
129
+ reason: DropReason;
130
+ };
131
+ export type TtlEvidenceRow = {
132
+ providerID: string;
133
+ modelID: string;
134
+ samples: number;
135
+ /** Largest idle gap that still hit the cache. */
136
+ maxHitGapMs: number | undefined;
137
+ /** Smallest idle gap after which reuse was lost. */
138
+ minDropGapMs: number | undefined;
139
+ };
140
+ export type LatencyStats = {
141
+ count: number;
142
+ p50: number | undefined;
143
+ p90: number | undefined;
144
+ p99: number | undefined;
145
+ };
146
+ export type LatencyReport = {
147
+ /** Milliseconds from request to completed response. */
148
+ wall: LatencyStats;
149
+ /** Milliseconds from request to the last streamed chunk (the streaming window). */
150
+ stream: LatencyStats;
151
+ /** Output-plus-reasoning tokens per second over that window. */
152
+ tps: LatencyStats;
153
+ };
154
+ /** Nearest-rank percentile over an unsorted sample. */
155
+ export declare function percentile(values: readonly number[], p: number): number | undefined;
156
+ export declare function latencyStats(values: number[]): LatencyStats;
157
+ export type SessionUsage = Usage & {
158
+ sessionID: string;
159
+ parentID: string | undefined;
160
+ title: string | undefined;
161
+ directory: string | undefined;
162
+ providers: string[];
163
+ models: string[];
164
+ prompts: number;
165
+ firstAt: number | undefined;
166
+ lastAt: number | undefined;
167
+ cacheHitRatio: number | undefined;
168
+ };
169
+ export declare function sessions(db: Database, options?: {
170
+ range?: Range;
171
+ filters?: Filters;
172
+ limit?: number;
173
+ sessionIDs?: readonly string[];
174
+ }): SessionUsage[];
175
+ /** Sessions whose parent is `parentID`: the subagents of one session. */
176
+ export declare function childSessionIDs(db: Database, parentID: string): string[];
177
+ /** Session usage rows for a specific set of sessions (subagent rollups). */
178
+ export declare function sessionUsageOf(db: Database, sessionIDs: readonly string[]): SessionUsage[];
179
+ export type TurnDrop = {
180
+ messageID: string;
181
+ tokens: number;
182
+ gapMs: number | undefined;
183
+ };
184
+ /**
185
+ * One finished turn of a session, built from the recorded steps and prompts. It
186
+ * carries the same buckets the telemetry line uses, so the panel can render the
187
+ * per-prompt history with the line's own formatter.
188
+ */
189
+ export type TurnUsage = {
190
+ sessionID: string;
191
+ /** Creation time of the prompt that opened the turn, when it was recorded. */
192
+ promptAt: number | undefined;
193
+ endAt: number | undefined;
194
+ /** Time between the previous prompt of the session and this turn's prompt. */
195
+ promptGapMs: number | undefined;
196
+ providerID: string | undefined;
197
+ modelID: string | undefined;
198
+ variant: string | undefined;
199
+ router: string | undefined;
200
+ /** The displayed identity differs from the previous turn of the session. */
201
+ routerChanged: boolean;
202
+ providerChanged: boolean;
203
+ modelChanged: boolean;
204
+ steps: number;
205
+ /** Raw buckets of the last step: the prompt the turn ended with. */
206
+ latest: Usage;
207
+ /** Raw buckets summed over every step of the turn. */
208
+ totals: Usage;
209
+ drops: TurnDrop[];
210
+ /** Finish reason of the turn's last step. */
211
+ finish: string | undefined;
212
+ /**
213
+ * Tokens generated per second of streaming over the turn's steps: produced
214
+ * tokens divided by their summed streaming windows. For a one-step turn it is
215
+ * that step's rate.
216
+ */
217
+ tps: number | undefined;
218
+ };
219
+ /**
220
+ * Finished turns, newest first. Steps belong to the newest prompt recorded at or
221
+ * before them; a turn whose last step asked for tools is dropped, because it is
222
+ * still running — the same rule the telemetry line follows.
223
+ *
224
+ * For one session the read is bounded by a window: only the steps at or after
225
+ * the `limit`-th newest prompt are loaded, so a session with tens of thousands
226
+ * of steps costs the same as a short one. The extra prompt is kept as the
227
+ * reference the oldest returned turn measures its gap against.
228
+ */
229
+ export declare function turns(db: Database, options?: {
230
+ sessionID?: string | undefined;
231
+ range?: Range | undefined;
232
+ filters?: Filters | undefined;
233
+ limit?: number | undefined;
234
+ }): TurnUsage[];
235
+ /**
236
+ * Routing endpoint of the newest step recorded before `at`. The TUI compares
237
+ * it with the endpoint of the turn it is about to render, because the same
238
+ * provider and model can be routed to a different endpoint per request.
239
+ */
240
+ export declare function routerBefore(db: Database, sessionID: string, at: number | undefined): string | undefined;
241
+ /**
242
+ * Routing endpoint of one turn: the newest endpoint the recorder stored for
243
+ * the given steps of a session. The TUI reads this for the telemetry line
244
+ * because the line renders from the TUI process while the recorder (the server
245
+ * process) is still writing the step that just finished. Scoped to the turn's
246
+ * own steps on purpose: the same provider and model can be routed to a
247
+ * different endpoint per request, so the newest endpoint of the session is not
248
+ * necessarily the one that served the turn being rendered.
249
+ */
250
+ export declare function routerForSteps(db: Database, sessionID: string, messageIDs: readonly string[]): string | undefined;
251
+ export type FilterOption = {
252
+ value: string;
253
+ steps: number;
254
+ };
255
+ /** Distinct models and endpoints, most used first. */
256
+ export declare function filterOptions(db: Database): {
257
+ models: FilterOption[];
258
+ endpoints: FilterOption[];
259
+ };
package/dist/text.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Text hygiene for everything that reaches a terminal.
3
+ *
4
+ * Some of what the plugin displays is not written by the user: a gateway names
5
+ * its own endpoint in a response header, a model writes a session title, an
6
+ * NDJSON export can come from anywhere. A control character in any of those is
7
+ * interpreted by the terminal that renders it (title changes, cursor moves,
8
+ * colours), so every such string is stripped of control characters before it
9
+ * is stored or drawn.
10
+ *
11
+ * The rule is deliberately blunt: drop C0 controls, DEL and the C1 block, keep
12
+ * everything else (the text is otherwise arbitrary Unicode), and cap the
13
+ * length so a hostile value cannot bloat the database or the frame. Layout is
14
+ * never touched: whitespace is preserved, because callers build indentation
15
+ * into the strings they truncate.
16
+ */
17
+ /** Longest value kept by default: a title, an id or a path, not a document. */
18
+ export declare const TEXT_LIMIT = 512;
19
+ /**
20
+ * Strip control characters and cap the length. Newlines and tabs go too: a
21
+ * line is one line, and the renderers lay out their own spacing.
22
+ */
23
+ export declare function sanitizeText(value: string, limit?: number): string;
24
+ /** `sanitizeText` without the layout, for a stored label: trimmed as well. */
25
+ export declare function sanitizeLabel(value: string, limit?: number): string;
26
+ export declare function isToken(value: string, limit?: number): boolean;
27
+ /** The lowercase form of a token identity, so the same endpoint groups once. */
28
+ export declare function normalizeToken(value: string, limit?: number): string | undefined;
package/dist/tui.d.ts ADDED
@@ -0,0 +1,58 @@
1
+ /** @jsxImportSource @opentui/solid */
2
+ import { Plugin } from "@opencode/plugin/tui";
3
+ import { type LineFields, type LineIdentity, type LineLabels, type TurnStats, type TurnTotalsView } from "./core";
4
+ import { type PanelView, type RangeKey } from "./panel-data";
5
+ /**
6
+ * TUI side of the plugin. Two contributions:
7
+ *
8
+ * - `session.composer.top`: one telemetry line after every turn with the end
9
+ * time, the delta since the previous prompt, the endpoint that served it,
10
+ * turn and session token totals, and cache warnings.
11
+ * - `session.panel` + the `/stats` slash command: the statistics panel over
12
+ * the database written by the server entry.
13
+ *
14
+ * The database is opened lazily and read-only in spirit: the TUI never
15
+ * writes usage. When the database cannot be opened, the line falls back to
16
+ * host data only and the panel explains itself.
17
+ */
18
+ export type Options = {
19
+ /** Turn the TUI side off: no line and no panel. */
20
+ enabled?: boolean | undefined;
21
+ /** SQLite database path override; must match the server entry. */
22
+ db?: string | undefined;
23
+ /**
24
+ * Which identity the line leads with: `endpoint` (default) shows the gateway
25
+ * endpoint that served the turn (`orcarouter-dsv4.1flash`) and warns when it
26
+ * changes; `model` shows `provider/model#variant` as before.
27
+ */
28
+ identity?: LineIdentity | undefined;
29
+ /**
30
+ * How the turn segment counts prompt usage: `latest` (default) shows the
31
+ * prompt size the turn ended with, `sum` adds every step of the turn.
32
+ */
33
+ turnTotals?: TurnTotalsView | undefined;
34
+ /** Telemetry line field toggles. */
35
+ fields?: Partial<LineFields> | undefined;
36
+ /** Telemetry line label overrides. */
37
+ labels?: Partial<LineLabels> | undefined;
38
+ /** Show the per-prompt history in the session sidebar (default true). */
39
+ sidebar?: boolean | undefined;
40
+ /** How many turns the sidebar history shows (default 5). */
41
+ sidebarTurns?: number | undefined;
42
+ /**
43
+ * Show the `/usage` statistics panel and its command (default true).
44
+ * `false` keeps the live line and the sidebar history.
45
+ */
46
+ panel?: boolean | undefined;
47
+ };
48
+ export declare const __test: {
49
+ line: ((sessionID: string) => TurnStats | undefined) | undefined;
50
+ lineText: ((sessionID: string) => string) | undefined;
51
+ open: ((input?: string) => boolean) | undefined;
52
+ state: (() => {
53
+ view: PanelView;
54
+ range: RangeKey;
55
+ }) | undefined;
56
+ };
57
+ declare const _default: Plugin.Definition;
58
+ export default _default;