@kitlangton/terminal-control 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kit Langton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @kitlangton/terminal-control
2
+
3
+ Typed terminal application control and testing client for `termctrl driver`, with stable screen snapshots, keyboard interaction, readable logs, recordings, and opt-in failure evidence.
4
+
5
+ Install the package with Vitest after the initial npm publication:
6
+
7
+ ```bash
8
+ bun add -d @kitlangton/terminal-control vitest
9
+ ```
10
+
11
+ The matching native `termctrl` binary is installed automatically on macOS or GNU/Linux on arm64 or x64:
12
+
13
+ ```ts
14
+ import { TerminalControl } from "@kitlangton/terminal-control"
15
+
16
+ await using terminal = await TerminalControl.make()
17
+ ```
18
+
19
+ For development or custom native builds, the runtime resolves an explicit `binaryPath` first, then `TERMCTRL_BINARY`, before the installed native package.
20
+
21
+ Visible screen text and frames are stable snapshot surfaces:
22
+
23
+ ```ts
24
+ await using session = await terminal.launch({ command: ["my-tui"] })
25
+ await session.screen.waitForText("Ready")
26
+ expect(await session.screen.text()).toMatchSnapshot()
27
+ ```
28
+
29
+ For line-oriented output, use `session.logs.text()`; for exact ANSI/VT bytes, use `session.transcript.ansi()`. Artifact and recording configuration is opt-in because terminal output and input may contain secrets. See the repository `README.md` for the complete workflow.
@@ -0,0 +1,217 @@
1
+ export type Color = {
2
+ r: number;
3
+ g: number;
4
+ b: number;
5
+ };
6
+ export type Attributes = {
7
+ bold: boolean;
8
+ italic: boolean;
9
+ faint: boolean;
10
+ invisible: boolean;
11
+ strikethrough: boolean;
12
+ overline: boolean;
13
+ underline: "single" | null;
14
+ };
15
+ export type Cell = {
16
+ x: number;
17
+ y: number;
18
+ text: string;
19
+ width: number;
20
+ foreground: Color;
21
+ background: Color;
22
+ attributes: Attributes;
23
+ };
24
+ export type Cursor = {
25
+ x: number;
26
+ y: number;
27
+ color: Color;
28
+ blinking: boolean;
29
+ };
30
+ export type Frame = {
31
+ version: 1;
32
+ cols: number;
33
+ rows: number;
34
+ foreground: Color;
35
+ background: Color;
36
+ cursor: Cursor | null;
37
+ cells: Cell[];
38
+ };
39
+ export type ScreenSnapshot = {
40
+ reason: CaptureReason;
41
+ frame: Frame;
42
+ text: string;
43
+ ansi?: Uint8Array;
44
+ svg?: string;
45
+ };
46
+ export type CaptureReason = "idle" | "deadline" | "exited" | "outputclosed";
47
+ export type CaptureResult = ScreenSnapshot;
48
+ export type ProcessExit = {
49
+ code: number;
50
+ signal: string | null;
51
+ success: boolean;
52
+ };
53
+ export type SessionStatus = {
54
+ state: "running" | "exited";
55
+ exit: ProcessExit | null;
56
+ cols: number;
57
+ rows: number;
58
+ cellWidth: number;
59
+ cellHeight: number;
60
+ idleForMs: number | null;
61
+ hasVisibleContent: boolean;
62
+ recording: boolean;
63
+ logsTruncated: boolean;
64
+ };
65
+ export type ControlLetter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
66
+ export type Key = "Enter" | "Escape" | "ArrowUp" | "ArrowDown" | "ArrowLeft" | "ArrowRight" | "Tab" | "Shift+Tab" | "Backspace" | "Delete" | "Home" | "End" | "PageUp" | "PageDown" | `Control+${Uppercase<ControlLetter>}`;
67
+ export type Viewport = {
68
+ cols: number;
69
+ rows: number;
70
+ cellWidth?: number;
71
+ cellHeight?: number;
72
+ };
73
+ export type LaunchOptions = {
74
+ command: readonly [string, ...string[]];
75
+ cwd?: string;
76
+ record?: string | true | "on-failure";
77
+ viewport?: Viewport;
78
+ host?: "opentui";
79
+ color?: "auto" | "always" | "never";
80
+ maxBytes?: number;
81
+ env?: Readonly<Record<string, string>>;
82
+ inheritEnv?: boolean;
83
+ };
84
+ export type ArtifactOptions = {
85
+ directory: string;
86
+ onFailure?: boolean;
87
+ includeTranscript?: boolean;
88
+ includeRecording?: boolean;
89
+ };
90
+ export type DriverOptions = {
91
+ binaryPath?: string;
92
+ cwd?: string;
93
+ env?: Record<string, string | undefined>;
94
+ artifacts?: false | ArtifactOptions;
95
+ };
96
+ export type WaitOptions = {
97
+ timeoutMs?: number;
98
+ };
99
+ export type IdleOptions = WaitOptions & {
100
+ quietForMs?: number;
101
+ };
102
+ export type CaptureOptions = {
103
+ settleMs?: number;
104
+ deadlineMs?: number;
105
+ allowIncomplete?: boolean;
106
+ includeAnsi?: boolean;
107
+ includeSvg?: boolean;
108
+ };
109
+ export type StableCaptureOptions = Omit<CaptureOptions, "allowIncomplete">;
110
+ export type WaitForExitResult = {
111
+ reason: "exited";
112
+ exit: ProcessExit;
113
+ } | {
114
+ reason: "deadline";
115
+ };
116
+ export type ArtifactManifest = {
117
+ directory: string;
118
+ screenText: string;
119
+ screenFrame: string;
120
+ screenSvg: string;
121
+ metadata: string;
122
+ logsText: string;
123
+ transcript?: string;
124
+ recording?: string;
125
+ };
126
+ type DriverRequest = <T>(method: string, params?: unknown, sessionId?: string) => Promise<T>;
127
+ export declare class TerminalControlError extends Error {
128
+ readonly code: string;
129
+ constructor(code: string, message: string);
130
+ }
131
+ export declare class IncompleteCaptureError extends Error {
132
+ readonly capture: CaptureResult;
133
+ constructor(capture: CaptureResult);
134
+ }
135
+ export declare function resolveTerminalControlBinary(explicit?: string): string;
136
+ export declare class TerminalControl implements AsyncDisposable {
137
+ private readonly child;
138
+ private readonly pending;
139
+ private readonly ready;
140
+ private nextRequestId;
141
+ private nextSessionId;
142
+ private closed;
143
+ private stderr;
144
+ private readySettled;
145
+ private rejectReady;
146
+ private readonly artifacts;
147
+ private constructor();
148
+ static make(options?: DriverOptions): Promise<TerminalControl>;
149
+ launch(options: LaunchOptions): Promise<Session>;
150
+ close(): Promise<void>;
151
+ [Symbol.asyncDispose](): Promise<void>;
152
+ private request;
153
+ private failAll;
154
+ private abort;
155
+ }
156
+ export declare class Session implements AsyncDisposable {
157
+ private readonly request;
158
+ readonly id: string;
159
+ private readonly artifacts;
160
+ private readonly temporaryRecording?;
161
+ private readonly launchOptions?;
162
+ readonly screen: Screen;
163
+ readonly keyboard: Keyboard;
164
+ readonly logs: Logs;
165
+ readonly transcript: Transcript;
166
+ private stopped;
167
+ constructor(request: DriverRequest, id: string, artifacts?: false | ArtifactOptions, temporaryRecording?: string | undefined, launchOptions?: LaunchOptions | undefined);
168
+ status(): Promise<SessionStatus>;
169
+ waitForExit(options?: WaitOptions): Promise<WaitForExitResult>;
170
+ resize(viewport: Viewport): Promise<void>;
171
+ recording(): Promise<Uint8Array>;
172
+ saveRecording(path: string): Promise<void>;
173
+ shouldWriteFailureArtifacts(): boolean;
174
+ writeArtifacts(name?: string): Promise<ArtifactManifest>;
175
+ withArtifactsOnFailure<T>(name: string, run: () => Promise<T>): Promise<T>;
176
+ stop(): Promise<void>;
177
+ [Symbol.asyncDispose](): Promise<void>;
178
+ }
179
+ export declare class Screen {
180
+ private readonly request;
181
+ private readonly sessionId;
182
+ constructor(request: DriverRequest, sessionId: string);
183
+ waitForText(text: string | RegExp, options?: WaitOptions): Promise<void>;
184
+ waitForIdle(options?: IdleOptions): Promise<void>;
185
+ waitUntil(predicate: (snapshot: ScreenSnapshot) => boolean | Promise<boolean>, options?: WaitOptions): Promise<ScreenSnapshot>;
186
+ capture(options?: CaptureOptions): Promise<ScreenSnapshot>;
187
+ frame(options?: StableCaptureOptions): Promise<Frame>;
188
+ text(options?: StableCaptureOptions): Promise<string>;
189
+ }
190
+ export declare class Keyboard {
191
+ private readonly request;
192
+ private readonly sessionId;
193
+ constructor(request: DriverRequest, sessionId: string);
194
+ type(value: string, options?: {
195
+ paceMs?: number;
196
+ }): Promise<void>;
197
+ press(key: Key): Promise<void>;
198
+ sequence(keys: readonly Key[], options?: {
199
+ paceMs?: number;
200
+ }): Promise<void>;
201
+ write(value: Uint8Array): Promise<void>;
202
+ private send;
203
+ }
204
+ export declare class Logs {
205
+ private readonly request;
206
+ private readonly sessionId;
207
+ constructor(request: DriverRequest, sessionId: string);
208
+ text(): Promise<string>;
209
+ }
210
+ export declare class Transcript {
211
+ private readonly request;
212
+ private readonly sessionId;
213
+ constructor(request: DriverRequest, sessionId: string);
214
+ ansi(): Promise<Uint8Array>;
215
+ }
216
+ export {};
217
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,462 @@
1
+ import { spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import { createRequire } from "node:module";
4
+ import { mkdir, rm, writeFile } from "node:fs/promises";
5
+ import { tmpdir } from "node:os";
6
+ import { dirname, join } from "node:path";
7
+ import { createInterface } from "node:readline";
8
+ export class TerminalControlError extends Error {
9
+ code;
10
+ constructor(code, message) {
11
+ super(message);
12
+ this.name = "TerminalControlError";
13
+ this.code = code;
14
+ }
15
+ }
16
+ export class IncompleteCaptureError extends Error {
17
+ capture;
18
+ constructor(capture) {
19
+ super(`screen capture completed with ${capture.reason} instead of a settled frame`);
20
+ this.name = "IncompleteCaptureError";
21
+ this.capture = capture;
22
+ }
23
+ }
24
+ const nativePackages = {
25
+ "darwin-arm64": "@kitlangton/terminal-control-darwin-arm64",
26
+ "darwin-x64": "@kitlangton/terminal-control-darwin-x64",
27
+ "linux-arm64": "@kitlangton/terminal-control-linux-arm64-gnu",
28
+ "linux-x64": "@kitlangton/terminal-control-linux-x64-gnu",
29
+ };
30
+ export function resolveTerminalControlBinary(explicit) {
31
+ if (explicit)
32
+ return explicit;
33
+ if (process.env.TERMCTRL_BINARY)
34
+ return process.env.TERMCTRL_BINARY;
35
+ const target = `${process.platform}-${process.arch}`;
36
+ const packageName = nativePackages[target];
37
+ if (!packageName) {
38
+ throw new Error(`no packaged termctrl binary is available for ${target}; provide binaryPath`);
39
+ }
40
+ try {
41
+ return createRequire(import.meta.url).resolve(`${packageName}/bin/termctrl`);
42
+ }
43
+ catch {
44
+ throw new Error(`termctrl native package ${packageName} is not installed; provide binaryPath`);
45
+ }
46
+ }
47
+ export class TerminalControl {
48
+ child;
49
+ pending = new Map();
50
+ ready;
51
+ nextRequestId = 1;
52
+ nextSessionId = 1;
53
+ closed = false;
54
+ stderr = "";
55
+ readySettled = false;
56
+ rejectReady = () => { };
57
+ artifacts;
58
+ constructor(options) {
59
+ this.artifacts = options.artifacts ?? false;
60
+ this.child = spawn(resolveTerminalControlBinary(options.binaryPath), ["driver"], {
61
+ cwd: options.cwd,
62
+ env: { ...process.env, ...options.env },
63
+ stdio: "pipe",
64
+ });
65
+ this.ready = new Promise((resolve, reject) => {
66
+ this.rejectReady = reject;
67
+ const lines = createInterface({ input: this.child.stdout });
68
+ lines.on("line", (line) => {
69
+ let message;
70
+ try {
71
+ message = JSON.parse(line);
72
+ }
73
+ catch (error) {
74
+ this.abort(new Error(`invalid termctrl driver response: ${String(error)}`));
75
+ return;
76
+ }
77
+ if (message.type === "hello") {
78
+ if (this.readySettled) {
79
+ this.abort(new Error("termctrl driver sent more than one hello message"));
80
+ return;
81
+ }
82
+ if (message.protocolVersion !== 1) {
83
+ this.abort(new Error(`unsupported termctrl protocol version ${message.protocolVersion}`));
84
+ return;
85
+ }
86
+ this.readySettled = true;
87
+ resolve(message);
88
+ return;
89
+ }
90
+ if (message.type === "error") {
91
+ if (message.id === null) {
92
+ this.abort(new TerminalControlError(message.error.code, message.error.message));
93
+ return;
94
+ }
95
+ const pending = this.pending.get(message.id);
96
+ if (!pending)
97
+ return;
98
+ this.pending.delete(message.id);
99
+ pending.reject(new TerminalControlError(message.error.code, message.error.message));
100
+ return;
101
+ }
102
+ const pending = this.pending.get(message.id);
103
+ if (!pending)
104
+ return;
105
+ this.pending.delete(message.id);
106
+ pending.resolve(message.result);
107
+ });
108
+ this.child.once("error", (error) => this.abort(error));
109
+ });
110
+ this.child.stderr.on("data", (chunk) => {
111
+ this.stderr = (this.stderr + new TextDecoder().decode(chunk)).slice(-8_192);
112
+ });
113
+ this.child.once("exit", (code, signal) => {
114
+ if (this.closed)
115
+ return;
116
+ const detail = this.stderr.trim();
117
+ const error = new Error(`termctrl driver exited (${signal ?? code ?? "unknown"})${detail ? `: ${detail}` : ""}`);
118
+ this.abort(error);
119
+ });
120
+ }
121
+ static async make(options = {}) {
122
+ const client = new TerminalControl(options);
123
+ await client.ready;
124
+ return client;
125
+ }
126
+ async launch(options) {
127
+ const sessionId = `session-${this.nextSessionId++}`;
128
+ const temporaryRecording = options.record === true || options.record === "on-failure"
129
+ ? join(tmpdir(), `termctrl-${process.pid}-${randomUUID()}.termctrl`)
130
+ : undefined;
131
+ const record = temporaryRecording ?? options.record;
132
+ await this.request("launch", {
133
+ command: [...options.command],
134
+ cwd: options.cwd,
135
+ record,
136
+ cols: options.viewport?.cols,
137
+ rows: options.viewport?.rows,
138
+ cellWidth: options.viewport?.cellWidth,
139
+ cellHeight: options.viewport?.cellHeight,
140
+ host: options.host,
141
+ color: options.color,
142
+ maxBytes: options.maxBytes,
143
+ env: options.env,
144
+ inheritEnv: options.inheritEnv,
145
+ }, sessionId);
146
+ return new Session((method, params, id) => this.request(method, params, id), sessionId, this.artifacts, temporaryRecording, options);
147
+ }
148
+ async close() {
149
+ if (this.closed)
150
+ return;
151
+ await this.request("shutdown");
152
+ this.child.stdin.end();
153
+ this.closed = true;
154
+ }
155
+ async [Symbol.asyncDispose]() {
156
+ await this.close();
157
+ }
158
+ async request(method, params, sessionId) {
159
+ await this.ready;
160
+ if (this.closed)
161
+ throw new Error("termctrl driver is closed");
162
+ const id = this.nextRequestId++;
163
+ const result = new Promise((resolve, reject) => {
164
+ this.pending.set(id, { resolve, reject });
165
+ });
166
+ const request = JSON.stringify({ id, method, sessionId, ...(params === undefined ? {} : { params }) });
167
+ this.child.stdin.write(`${request}\n`, (error) => {
168
+ if (!error)
169
+ return;
170
+ const pending = this.pending.get(id);
171
+ this.pending.delete(id);
172
+ pending?.reject(error);
173
+ this.abort(error);
174
+ });
175
+ return result;
176
+ }
177
+ failAll(error) {
178
+ for (const pending of this.pending.values())
179
+ pending.reject(error);
180
+ this.pending.clear();
181
+ }
182
+ abort(error) {
183
+ if (this.closed)
184
+ return;
185
+ this.closed = true;
186
+ if (!this.readySettled) {
187
+ this.readySettled = true;
188
+ this.rejectReady(error);
189
+ }
190
+ this.failAll(error);
191
+ this.child.stdin.destroy();
192
+ this.child.kill();
193
+ }
194
+ }
195
+ export class Session {
196
+ request;
197
+ id;
198
+ artifacts;
199
+ temporaryRecording;
200
+ launchOptions;
201
+ screen;
202
+ keyboard;
203
+ logs;
204
+ transcript;
205
+ stopped = false;
206
+ constructor(request, id, artifacts = false, temporaryRecording, launchOptions) {
207
+ this.request = request;
208
+ this.id = id;
209
+ this.artifacts = artifacts;
210
+ this.temporaryRecording = temporaryRecording;
211
+ this.launchOptions = launchOptions;
212
+ this.screen = new Screen(request, id);
213
+ this.keyboard = new Keyboard(request, id);
214
+ this.logs = new Logs(request, id);
215
+ this.transcript = new Transcript(request, id);
216
+ }
217
+ status() {
218
+ return this.request("status", undefined, this.id).then((status) => ({
219
+ state: status.state,
220
+ exit: status.exit,
221
+ cols: status.cols,
222
+ rows: status.rows,
223
+ cellWidth: status.cell_width,
224
+ cellHeight: status.cell_height,
225
+ idleForMs: status.idle_for_ms,
226
+ hasVisibleContent: status.has_visible_content,
227
+ recording: status.recording,
228
+ logsTruncated: status.logs_truncated,
229
+ }));
230
+ }
231
+ waitForExit(options = {}) {
232
+ return this.request("waitForExit", { timeoutMs: options.timeoutMs }, this.id);
233
+ }
234
+ resize(viewport) {
235
+ return this.request("resize", {
236
+ cols: viewport.cols,
237
+ rows: viewport.rows,
238
+ cellWidth: viewport.cellWidth,
239
+ cellHeight: viewport.cellHeight,
240
+ }, this.id);
241
+ }
242
+ async recording() {
243
+ const result = await this.request("recording", undefined, this.id);
244
+ return Uint8Array.from(result.bytes);
245
+ }
246
+ async saveRecording(path) {
247
+ await mkdir(dirname(path), { recursive: true });
248
+ await writeFile(path, await this.recording());
249
+ }
250
+ shouldWriteFailureArtifacts() {
251
+ return this.artifacts !== false && this.artifacts.onFailure !== false;
252
+ }
253
+ async writeArtifacts(name = this.id) {
254
+ if (!this.artifacts)
255
+ throw new Error("configure Terminal Control artifacts before writing failure evidence");
256
+ const directory = join(this.artifacts.directory, artifactName(name));
257
+ const includeTranscript = this.artifacts.includeTranscript === true;
258
+ const capture = await this.screen.capture({
259
+ allowIncomplete: true,
260
+ includeAnsi: includeTranscript,
261
+ includeSvg: true,
262
+ deadlineMs: 250,
263
+ });
264
+ const [logs, status] = await Promise.all([this.logs.text(), this.status()]);
265
+ await mkdir(directory, { recursive: true });
266
+ const manifest = {
267
+ directory,
268
+ screenText: join(directory, "screen.txt"),
269
+ screenFrame: join(directory, "screen.json"),
270
+ screenSvg: join(directory, "screen.svg"),
271
+ metadata: join(directory, "metadata.json"),
272
+ logsText: join(directory, "logs.txt"),
273
+ };
274
+ await Promise.all([
275
+ writeFile(manifest.screenText, capture.text),
276
+ writeFile(manifest.screenFrame, JSON.stringify(capture.frame, null, 2)),
277
+ writeFile(manifest.screenSvg, capture.svg ?? ""),
278
+ writeFile(manifest.logsText, logs),
279
+ writeFile(manifest.metadata, JSON.stringify({
280
+ sessionId: this.id,
281
+ captureReason: capture.reason,
282
+ launch: artifactLaunchOptions(this.launchOptions),
283
+ status,
284
+ }, null, 2)),
285
+ ]);
286
+ if (includeTranscript && capture.ansi) {
287
+ manifest.transcript = join(directory, "transcript.ansi");
288
+ await writeFile(manifest.transcript, capture.ansi);
289
+ }
290
+ if (this.artifacts.includeRecording && status.recording) {
291
+ manifest.recording = join(directory, "recording.termctrl");
292
+ await writeFile(manifest.recording, await this.recording());
293
+ }
294
+ return manifest;
295
+ }
296
+ async withArtifactsOnFailure(name, run) {
297
+ try {
298
+ return await run();
299
+ }
300
+ catch (error) {
301
+ if (this.shouldWriteFailureArtifacts())
302
+ await this.writeArtifacts(name);
303
+ throw error;
304
+ }
305
+ }
306
+ async stop() {
307
+ if (this.stopped)
308
+ return;
309
+ await this.request("stop", undefined, this.id);
310
+ this.stopped = true;
311
+ if (this.temporaryRecording)
312
+ await rm(this.temporaryRecording, { force: true });
313
+ }
314
+ async [Symbol.asyncDispose]() {
315
+ await this.stop();
316
+ }
317
+ }
318
+ export class Screen {
319
+ request;
320
+ sessionId;
321
+ constructor(request, sessionId) {
322
+ this.request = request;
323
+ this.sessionId = sessionId;
324
+ }
325
+ waitForText(text, options = {}) {
326
+ if (typeof text === "string") {
327
+ return this.request("waitForText", { text, timeoutMs: options.timeoutMs }, this.sessionId);
328
+ }
329
+ return this.waitUntil((snapshot) => {
330
+ text.lastIndex = 0;
331
+ return text.test(snapshot.text);
332
+ }, options).then(() => undefined);
333
+ }
334
+ waitForIdle(options = {}) {
335
+ return this.request("waitForIdle", {
336
+ quietForMs: options.quietForMs,
337
+ timeoutMs: options.timeoutMs,
338
+ }, this.sessionId);
339
+ }
340
+ async waitUntil(predicate, options = {}) {
341
+ const timeoutMs = options.timeoutMs ?? 5_000;
342
+ const deadline = Date.now() + timeoutMs;
343
+ while (true) {
344
+ const snapshot = await this.capture({ allowIncomplete: true, settleMs: 0, deadlineMs: 0 });
345
+ if (await predicate(snapshot))
346
+ return snapshot;
347
+ if (Date.now() >= deadline)
348
+ throw new Error("timed out waiting for visible terminal predicate");
349
+ await new Promise((resolve) => setTimeout(resolve, 10));
350
+ }
351
+ }
352
+ async capture(options = {}) {
353
+ const wire = await this.request("capture", {
354
+ settleMs: options.settleMs,
355
+ deadlineMs: options.deadlineMs,
356
+ includeAnsi: options.includeAnsi === true,
357
+ includeSvg: options.includeSvg === true,
358
+ }, this.sessionId);
359
+ const capture = {
360
+ reason: wire.reason,
361
+ frame: wire.shot.frame,
362
+ text: wire.shot.text,
363
+ ...(options.includeAnsi ? { ansi: Uint8Array.from(wire.shot.ansi) } : {}),
364
+ ...(wire.shot.svg ? { svg: wire.shot.svg } : {}),
365
+ };
366
+ if (!options.allowIncomplete && (capture.reason === "deadline" || capture.reason === "outputclosed")) {
367
+ throw new IncompleteCaptureError(capture);
368
+ }
369
+ return capture;
370
+ }
371
+ async frame(options = {}) {
372
+ return (await this.capture(options)).frame;
373
+ }
374
+ async text(options = {}) {
375
+ return (await this.capture(options)).text;
376
+ }
377
+ }
378
+ export class Keyboard {
379
+ request;
380
+ sessionId;
381
+ constructor(request, sessionId) {
382
+ this.request = request;
383
+ this.sessionId = sessionId;
384
+ }
385
+ type(value, options = {}) {
386
+ return this.send([{ type: "text", value }], options.paceMs);
387
+ }
388
+ press(key) {
389
+ return this.send([keyAtom(key)]);
390
+ }
391
+ sequence(keys, options = {}) {
392
+ return this.send(keys.map(keyAtom), options.paceMs);
393
+ }
394
+ write(value) {
395
+ return this.send([{ type: "bytes", value: Array.from(value) }]);
396
+ }
397
+ send(input, paceMs = 0) {
398
+ return this.request("send", { input, paceMs }, this.sessionId);
399
+ }
400
+ }
401
+ export class Logs {
402
+ request;
403
+ sessionId;
404
+ constructor(request, sessionId) {
405
+ this.request = request;
406
+ this.sessionId = sessionId;
407
+ }
408
+ async text() {
409
+ const result = await this.request("logs", { ansi: false }, this.sessionId);
410
+ return new TextDecoder().decode(Uint8Array.from(result.bytes));
411
+ }
412
+ }
413
+ export class Transcript {
414
+ request;
415
+ sessionId;
416
+ constructor(request, sessionId) {
417
+ this.request = request;
418
+ this.sessionId = sessionId;
419
+ }
420
+ async ansi() {
421
+ const result = await this.request("logs", { ansi: true }, this.sessionId);
422
+ return Uint8Array.from(result.bytes);
423
+ }
424
+ }
425
+ const namedKeys = {
426
+ Enter: "enter",
427
+ Escape: "escape",
428
+ ArrowUp: "arrowUp",
429
+ ArrowDown: "arrowDown",
430
+ ArrowLeft: "arrowLeft",
431
+ ArrowRight: "arrowRight",
432
+ Tab: "tab",
433
+ "Shift+Tab": "shiftTab",
434
+ Backspace: "backspace",
435
+ Delete: "delete",
436
+ Home: "home",
437
+ End: "end",
438
+ PageUp: "pageUp",
439
+ PageDown: "pageDown",
440
+ };
441
+ function keyAtom(key) {
442
+ if (isControlKey(key)) {
443
+ return { type: "control", value: key.slice("Control+".length).toLowerCase() };
444
+ }
445
+ return { type: "key", value: namedKeys[key] };
446
+ }
447
+ function isControlKey(key) {
448
+ return key.startsWith("Control+");
449
+ }
450
+ function artifactName(name) {
451
+ const safe = name.replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
452
+ return safe || "failure";
453
+ }
454
+ function artifactLaunchOptions(options) {
455
+ if (!options?.env)
456
+ return options;
457
+ return {
458
+ ...options,
459
+ env: Object.fromEntries(Object.keys(options.env).map((key) => [key, "[redacted]"])),
460
+ };
461
+ }
462
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ type MatcherResult = {
2
+ pass: boolean;
3
+ message: () => string;
4
+ };
5
+ type MatcherRegistry = {
6
+ extend(matchers: Record<string, (received: unknown, expected: string) => Promise<MatcherResult>>): void;
7
+ };
8
+ export declare const terminalControlMatchers: {
9
+ toHaveScreenText(received: unknown, expected: string): Promise<MatcherResult>;
10
+ };
11
+ export declare function extendTerminalControlMatchers(expect: MatcherRegistry): void;
12
+ declare module "vitest" {
13
+ interface Assertion<T> {
14
+ toHaveScreenText(expected: string): Promise<void>;
15
+ }
16
+ interface AsymmetricMatchersContaining {
17
+ toHaveScreenText(expected: string): Promise<void>;
18
+ }
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAGA,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,MAAM,CAAA;CACtB,CAAA;AAED,KAAK,eAAe,GAAG;IACrB,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,IAAI,CAAA;CACxG,CAAA;AAED,eAAO,MAAM,uBAAuB;+BACD,OAAO,YAAY,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;CA8BpF,CAAA;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAE3E;AAED,OAAO,QAAQ,QAAQ,CAAC;IACtB,UAAU,SAAS,CAAC,CAAC;QACnB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAClD;IAED,UAAU,4BAA4B;QACpC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAClD;CACF"}
package/dist/vitest.js ADDED
@@ -0,0 +1,39 @@
1
+ import { IncompleteCaptureError, Screen, Session } from "./index.js";
2
+ export const terminalControlMatchers = {
3
+ async toHaveScreenText(received, expected) {
4
+ if (!(received instanceof Session) && !(received instanceof Screen)) {
5
+ return {
6
+ pass: false,
7
+ message: () => "toHaveScreenText expects a Terminal Control Session or Screen",
8
+ };
9
+ }
10
+ const screen = received instanceof Session ? received.screen : received;
11
+ let actual;
12
+ let incomplete = "";
13
+ try {
14
+ actual = await screen.text();
15
+ }
16
+ catch (error) {
17
+ if (!(error instanceof IncompleteCaptureError))
18
+ throw error;
19
+ actual = error.capture.text;
20
+ incomplete = `\nCapture was incomplete: ${error.capture.reason}.`;
21
+ }
22
+ const pass = incomplete === "" && actual === expected;
23
+ let artifact = "";
24
+ if (!pass && received instanceof Session && received.shouldWriteFailureArtifacts()) {
25
+ const files = await received.writeArtifacts("screen-text");
26
+ artifact = `\nArtifacts: ${files.directory}`;
27
+ }
28
+ return {
29
+ pass,
30
+ message: () => pass
31
+ ? "expected visible terminal screen not to equal the supplied text"
32
+ : `expected visible terminal screen:\n${expected}\n\nreceived:\n${actual}${incomplete}${artifact}`,
33
+ };
34
+ },
35
+ };
36
+ export function extendTerminalControlMatchers(expect) {
37
+ expect.extend(terminalControlMatchers);
38
+ }
39
+ //# sourceMappingURL=vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAYpE,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,KAAK,CAAC,gBAAgB,CAAC,QAAiB,EAAE,QAAgB;QACxD,IAAI,CAAC,CAAC,QAAQ,YAAY,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,YAAY,MAAM,CAAC,EAAE,CAAC;YACpE,OAAO;gBACL,IAAI,EAAE,KAAK;gBACX,OAAO,EAAE,GAAG,EAAE,CAAC,+DAA+D;aAC/E,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,QAAQ,YAAY,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAA;QACvE,IAAI,MAAc,CAAA;QAClB,IAAI,UAAU,GAAG,EAAE,CAAA;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,sBAAsB,CAAC;gBAAE,MAAM,KAAK,CAAA;YAC3D,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAA;YAC3B,UAAU,GAAG,6BAA6B,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAA;QACnE,CAAC;QACD,MAAM,IAAI,GAAG,UAAU,KAAK,EAAE,IAAI,MAAM,KAAK,QAAQ,CAAA;QACrD,IAAI,QAAQ,GAAG,EAAE,CAAA;QACjB,IAAI,CAAC,IAAI,IAAI,QAAQ,YAAY,OAAO,IAAI,QAAQ,CAAC,2BAA2B,EAAE,EAAE,CAAC;YACnF,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,CAAA;YAC1D,QAAQ,GAAG,gBAAgB,KAAK,CAAC,SAAS,EAAE,CAAA;QAC9C,CAAC;QACD,OAAO;YACL,IAAI;YACJ,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;gBACjB,CAAC,CAAC,iEAAiE;gBACnE,CAAC,CAAC,sCAAsC,QAAQ,kBAAkB,MAAM,GAAG,UAAU,GAAG,QAAQ,EAAE;SACrG,CAAA;IACH,CAAC;CACF,CAAA;AAED,MAAM,UAAU,6BAA6B,CAAC,MAAuB;IACnE,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAA;AACxC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@kitlangton/terminal-control",
3
+ "version": "0.1.0",
4
+ "description": "Typed terminal application control and testing with snapshots, recordings, and failure artifacts",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/kitlangton/terminal-control.git",
9
+ "directory": "packages/test"
10
+ },
11
+ "type": "module",
12
+ "files": [
13
+ "dist",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ },
22
+ "./vitest": {
23
+ "types": "./dist/vitest.d.ts",
24
+ "import": "./dist/vitest.js"
25
+ }
26
+ },
27
+ "types": "./dist/index.d.ts",
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "scripts": {
35
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
36
+ "test": "cargo build --manifest-path ../../Cargo.toml --bin termctrl && bun test src/index.test.ts",
37
+ "typecheck": "tsc --noEmit",
38
+ "prepack": "bun run build"
39
+ },
40
+ "optionalDependencies": {
41
+ "@kitlangton/terminal-control-darwin-arm64": "0.1.0",
42
+ "@kitlangton/terminal-control-darwin-x64": "0.1.0",
43
+ "@kitlangton/terminal-control-linux-arm64-gnu": "0.1.0",
44
+ "@kitlangton/terminal-control-linux-x64-gnu": "0.1.0"
45
+ },
46
+ "peerDependencies": {
47
+ "vitest": ">=3.0.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "vitest": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "@types/bun": "^1.3.0",
56
+ "@types/node": "^24.0.0",
57
+ "typescript": "^5.9.0",
58
+ "vitest": "^3.2.4"
59
+ }
60
+ }