@modelprofile.com/flexharness 6.1.0 → 7.0.1

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.
Files changed (169) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts_chat/classes.chatsession.d.ts +37 -0
  3. package/dist_ts_chat/classes.chatsession.js +102 -0
  4. package/dist_ts_chat/index.d.ts +2 -0
  5. package/dist_ts_chat/index.js +2 -0
  6. package/dist_ts_chat/interfaces.d.ts +72 -0
  7. package/dist_ts_chat/interfaces.js +2 -0
  8. package/dist_ts_chat/plugins.d.ts +4 -0
  9. package/dist_ts_chat/plugins.js +4 -0
  10. package/dist_ts_chat_cli/components.chatapp.d.ts +10 -0
  11. package/dist_ts_chat_cli/components.chatapp.js +75 -0
  12. package/dist_ts_chat_cli/components.inputarea.d.ts +7 -0
  13. package/dist_ts_chat_cli/components.inputarea.js +18 -0
  14. package/dist_ts_chat_cli/components.message.d.ts +12 -0
  15. package/dist_ts_chat_cli/components.message.js +21 -0
  16. package/dist_ts_chat_cli/components.messagelist.d.ts +9 -0
  17. package/dist_ts_chat_cli/components.messagelist.js +19 -0
  18. package/dist_ts_chat_cli/components.statusbar.d.ts +9 -0
  19. package/dist_ts_chat_cli/components.statusbar.js +15 -0
  20. package/dist_ts_chat_cli/index.d.ts +2 -0
  21. package/dist_ts_chat_cli/index.js +2 -0
  22. package/dist_ts_chat_cli/plugins.d.ts +12 -0
  23. package/dist_ts_chat_cli/plugins.js +14 -0
  24. package/dist_ts_chat_cli/startchat.d.ts +18 -0
  25. package/dist_ts_chat_cli/startchat.js +17 -0
  26. package/dist_ts_chat_web/flexchat-input.d.ts +21 -0
  27. package/dist_ts_chat_web/flexchat-input.js +172 -0
  28. package/dist_ts_chat_web/flexchat-message.d.ts +26 -0
  29. package/dist_ts_chat_web/flexchat-message.js +174 -0
  30. package/dist_ts_chat_web/flexchat-window.d.ts +34 -0
  31. package/dist_ts_chat_web/flexchat-window.js +394 -0
  32. package/dist_ts_chat_web/index.d.ts +3 -0
  33. package/dist_ts_chat_web/index.js +4 -0
  34. package/dist_ts_chat_web/plugins.d.ts +6 -0
  35. package/dist_ts_chat_web/plugins.js +4 -0
  36. package/dist_ts_compaction/index.d.ts +2 -0
  37. package/dist_ts_compaction/index.js +2 -0
  38. package/dist_ts_compaction/plugins.d.ts +4 -0
  39. package/dist_ts_compaction/plugins.js +3 -0
  40. package/dist_ts_compaction/smartagent.compaction.d.ts +14 -0
  41. package/dist_ts_compaction/smartagent.compaction.js +49 -0
  42. package/dist_ts_document/index.d.ts +19 -0
  43. package/dist_ts_document/index.js +55 -0
  44. package/dist_ts_document/plugins.d.ts +3 -0
  45. package/dist_ts_document/plugins.js +4 -0
  46. package/dist_ts_kvm/classes.browserkvm.d.ts +22 -0
  47. package/dist_ts_kvm/classes.browserkvm.js +271 -0
  48. package/dist_ts_kvm/classes.kvmterminal.d.ts +8 -0
  49. package/dist_ts_kvm/classes.kvmterminal.js +88 -0
  50. package/dist_ts_kvm/commandwrappers.d.ts +14 -0
  51. package/dist_ts_kvm/commandwrappers.js +58 -0
  52. package/dist_ts_kvm/index.d.ts +6 -0
  53. package/dist_ts_kvm/index.js +7 -0
  54. package/dist_ts_kvm/interfaces.d.ts +106 -0
  55. package/dist_ts_kvm/interfaces.js +2 -0
  56. package/dist_ts_kvm/ocr.d.ts +14 -0
  57. package/dist_ts_kvm/ocr.js +23 -0
  58. package/dist_ts_kvm/plugins.d.ts +6 -0
  59. package/dist_ts_kvm/plugins.js +10 -0
  60. package/dist_ts_kvm/tools.d.ts +15 -0
  61. package/dist_ts_kvm/tools.js +40 -0
  62. package/dist_ts_mcp/index.d.ts +86 -0
  63. package/dist_ts_mcp/index.js +284 -0
  64. package/dist_ts_mcp/requestcontext.d.ts +11 -0
  65. package/dist_ts_mcp/requestcontext.js +42 -0
  66. package/dist_ts_media/index.d.ts +8 -0
  67. package/dist_ts_media/index.js +21 -0
  68. package/dist_ts_media/plugins.d.ts +2 -0
  69. package/dist_ts_media/plugins.js +3 -0
  70. package/dist_ts_ocr/classes.imageocr.d.ts +19 -0
  71. package/dist_ts_ocr/classes.imageocr.js +27 -0
  72. package/dist_ts_ocr/index.d.ts +2 -0
  73. package/dist_ts_ocr/index.js +3 -0
  74. package/dist_ts_ocr/ocr.mistral.d.ts +75 -0
  75. package/dist_ts_ocr/ocr.mistral.js +77 -0
  76. package/dist_ts_tools/index.d.ts +17 -0
  77. package/dist_ts_tools/index.js +10 -0
  78. package/dist_ts_tools/plugins.d.ts +7 -0
  79. package/dist_ts_tools/plugins.js +6 -0
  80. package/dist_ts_tools/tool.browser.d.ts +11 -0
  81. package/dist_ts_tools/tool.browser.js +128 -0
  82. package/dist_ts_tools/tool.context.d.ts +4 -0
  83. package/dist_ts_tools/tool.context.js +23 -0
  84. package/dist_ts_tools/tool.filesystem.d.ts +11 -0
  85. package/dist_ts_tools/tool.filesystem.js +106 -0
  86. package/dist_ts_tools/tool.http.d.ts +2 -0
  87. package/dist_ts_tools/tool.http.js +65 -0
  88. package/dist_ts_tools/tool.json.d.ts +2 -0
  89. package/dist_ts_tools/tool.json.js +47 -0
  90. package/dist_ts_tools/tool.shell.d.ts +15 -0
  91. package/dist_ts_tools/tool.shell.js +329 -0
  92. package/dist_ts_tools/utils.truncation.d.ts +10 -0
  93. package/dist_ts_tools/utils.truncation.js +26 -0
  94. package/dist_ts_tools_node/index.d.ts +5 -0
  95. package/dist_ts_tools_node/index.js +4 -0
  96. package/dist_ts_tools_node/plugins.d.ts +13 -0
  97. package/dist_ts_tools_node/plugins.js +13 -0
  98. package/dist_ts_tools_node/tool.context.d.ts +31 -0
  99. package/dist_ts_tools_node/tool.context.js +837 -0
  100. package/dist_ts_tools_node/tool.persistence.d.ts +15 -0
  101. package/dist_ts_tools_node/tool.persistence.js +129 -0
  102. package/dist_ts_tools_node/tools.local.d.ts +11 -0
  103. package/dist_ts_tools_node/tools.local.js +15 -0
  104. package/package.json +112 -4
  105. package/readme.md +122 -66
  106. package/ts/00_commitinfo_data.ts +1 -1
  107. package/ts/readme.md +122 -66
  108. package/ts_chat/classes.chatsession.ts +116 -0
  109. package/ts_chat/index.ts +8 -0
  110. package/ts_chat/interfaces.ts +64 -0
  111. package/ts_chat/plugins.ts +7 -0
  112. package/ts_chat/readme.md +49 -0
  113. package/ts_chat_cli/components.chatapp.ts +103 -0
  114. package/ts_chat_cli/components.inputarea.ts +29 -0
  115. package/ts_chat_cli/components.message.ts +48 -0
  116. package/ts_chat_cli/components.messagelist.ts +45 -0
  117. package/ts_chat_cli/components.statusbar.ts +31 -0
  118. package/ts_chat_cli/index.ts +2 -0
  119. package/ts_chat_cli/plugins.ts +21 -0
  120. package/ts_chat_cli/readme.md +40 -0
  121. package/ts_chat_cli/startchat.ts +34 -0
  122. package/ts_chat_web/flexchat-input.ts +184 -0
  123. package/ts_chat_web/flexchat-message.ts +185 -0
  124. package/ts_chat_web/flexchat-window.ts +416 -0
  125. package/ts_chat_web/index.ts +3 -0
  126. package/ts_chat_web/plugins.ts +9 -0
  127. package/ts_chat_web/readme.md +46 -0
  128. package/ts_compaction/index.ts +2 -0
  129. package/ts_compaction/plugins.ts +6 -0
  130. package/ts_compaction/readme.md +37 -0
  131. package/ts_compaction/smartagent.compaction.ts +59 -0
  132. package/ts_document/index.ts +82 -0
  133. package/ts_document/plugins.ts +3 -0
  134. package/ts_document/readme.md +49 -0
  135. package/ts_kvm/classes.browserkvm.ts +335 -0
  136. package/ts_kvm/classes.kvmterminal.ts +103 -0
  137. package/ts_kvm/commandwrappers.ts +83 -0
  138. package/ts_kvm/index.ts +6 -0
  139. package/ts_kvm/interfaces.ts +152 -0
  140. package/ts_kvm/ocr.ts +41 -0
  141. package/ts_kvm/plugins.ts +13 -0
  142. package/ts_kvm/readme.md +482 -0
  143. package/ts_kvm/tools.ts +58 -0
  144. package/ts_mcp/index.ts +470 -0
  145. package/ts_mcp/readme.md +37 -0
  146. package/ts_mcp/requestcontext.ts +66 -0
  147. package/ts_media/index.ts +29 -0
  148. package/ts_media/plugins.ts +2 -0
  149. package/ts_media/readme.md +37 -0
  150. package/ts_ocr/classes.imageocr.ts +53 -0
  151. package/ts_ocr/index.ts +2 -0
  152. package/ts_ocr/ocr.mistral.ts +192 -0
  153. package/ts_ocr/readme.md +48 -0
  154. package/ts_tools/index.ts +49 -0
  155. package/ts_tools/plugins.ts +7 -0
  156. package/ts_tools/readme.md +37 -0
  157. package/ts_tools/tool.browser.ts +159 -0
  158. package/ts_tools/tool.context.ts +23 -0
  159. package/ts_tools/tool.filesystem.ts +144 -0
  160. package/ts_tools/tool.http.ts +78 -0
  161. package/ts_tools/tool.json.ts +53 -0
  162. package/ts_tools/tool.shell.ts +418 -0
  163. package/ts_tools/utils.truncation.ts +39 -0
  164. package/ts_tools_node/index.ts +5 -0
  165. package/ts_tools_node/plugins.ts +16 -0
  166. package/ts_tools_node/readme.md +37 -0
  167. package/ts_tools_node/tool.context.ts +991 -0
  168. package/ts_tools_node/tool.persistence.ts +146 -0
  169. package/ts_tools_node/tools.local.ts +26 -0
@@ -0,0 +1,152 @@
1
+ export type TKvmKind = 'jetkvm' | 'glinet' | 'pikvm' | 'tinypilot' | 'generic';
2
+
3
+ export type TKvmFrameMimeType = 'image/png' | 'image/jpeg';
4
+
5
+ export type TKvmOsHint = 'windows' | 'macos' | 'linux' | 'unknown';
6
+
7
+ export type TKvmShellHint = 'powershell' | 'cmd' | 'bash' | 'zsh' | 'sh' | 'unknown';
8
+
9
+ export type TKvmKey =
10
+ | 'Enter'
11
+ | 'Escape'
12
+ | 'Tab'
13
+ | 'Backspace'
14
+ | 'Delete'
15
+ | 'ArrowUp'
16
+ | 'ArrowDown'
17
+ | 'ArrowLeft'
18
+ | 'ArrowRight'
19
+ | 'Home'
20
+ | 'End'
21
+ | 'PageUp'
22
+ | 'PageDown'
23
+ | 'Space'
24
+ | 'Meta'
25
+ | 'Control'
26
+ | 'Alt'
27
+ | 'Shift'
28
+ | 'F1'
29
+ | 'F2'
30
+ | 'F3'
31
+ | 'F4'
32
+ | 'F5'
33
+ | 'F6'
34
+ | 'F7'
35
+ | 'F8'
36
+ | 'F9'
37
+ | 'F10'
38
+ | 'F11'
39
+ | 'F12'
40
+ | string;
41
+
42
+ export interface IKvmFrame {
43
+ timestamp: number;
44
+ width: number;
45
+ height: number;
46
+ mimeType: TKvmFrameMimeType;
47
+ dataBase64: string;
48
+ }
49
+
50
+ export interface IBrowserKvmOptions {
51
+ url: string;
52
+ kind?: TKvmKind;
53
+ username?: string;
54
+ password?: string;
55
+ headless?: boolean;
56
+ /**
57
+ * Main element that should receive keyboard focus.
58
+ * Usually video, canvas, or a wrapper around the KVM viewer.
59
+ */
60
+ viewerSelector?: string;
61
+ /**
62
+ * Element used for frame capture.
63
+ * Defaults to video, then canvas, then viewer screenshot fallback.
64
+ */
65
+ captureSelector?: string;
66
+ /**
67
+ * Useful for self-signed KVM certificates.
68
+ */
69
+ ignoreHttpsErrors?: boolean;
70
+ /**
71
+ * Persist browser session cookies/login state.
72
+ */
73
+ userDataDir?: string;
74
+ /**
75
+ * Optional browser executable path.
76
+ */
77
+ executablePath?: string;
78
+ /**
79
+ * Optional timeout for initial load and viewer detection.
80
+ */
81
+ timeoutMs?: number;
82
+ }
83
+
84
+ export interface IKvmTypeTextOptions {
85
+ delayMs?: number;
86
+ }
87
+
88
+ export interface IKvmDriver {
89
+ readonly kind: TKvmKind;
90
+ connect: () => Promise<void>;
91
+ disconnect: () => Promise<void>;
92
+ focusViewer: () => Promise<void>;
93
+ captureFrame: () => Promise<IKvmFrame>;
94
+ typeText: (text: string, options?: IKvmTypeTextOptions) => Promise<void>;
95
+ pressKey: (key: TKvmKey) => Promise<void>;
96
+ pressShortcut: (keys: TKvmKey[]) => Promise<void>;
97
+ wait: (milliseconds: number) => Promise<void>;
98
+ }
99
+
100
+ export interface IOcrCrop {
101
+ x: number;
102
+ y: number;
103
+ width: number;
104
+ height: number;
105
+ }
106
+
107
+ export interface IOcrRecognizeOptions {
108
+ crop?: IOcrCrop;
109
+ language?: string;
110
+ }
111
+
112
+ export interface IOcrResult {
113
+ text: string;
114
+ confidence?: number;
115
+ }
116
+
117
+ export interface IOcrEngine {
118
+ recognize: (frame: IKvmFrame, options?: IOcrRecognizeOptions) => Promise<IOcrResult>;
119
+ }
120
+
121
+ export interface IKvmTerminalOptions {
122
+ kvm: IKvmDriver;
123
+ ocrEngine: IOcrEngine;
124
+ osHint?: TKvmOsHint;
125
+ shellHint?: TKvmShellHint;
126
+ commandTimeoutMs?: number;
127
+ ocrPollIntervalMs?: number;
128
+ ocrMaxAttempts?: number;
129
+ /**
130
+ * Optional OCR crop to limit recognition to terminal area.
131
+ */
132
+ ocrCrop?: IOcrCrop;
133
+ }
134
+
135
+ export interface IKvmTerminalCommandResult {
136
+ commandId: string;
137
+ command: string;
138
+ completed: boolean;
139
+ timedOut: boolean;
140
+ exitCode?: number;
141
+ combinedText: string;
142
+ rawOcrText: string;
143
+ }
144
+
145
+ export interface IWrappedKvmCommand {
146
+ commandId: string;
147
+ shellHint: TKvmShellHint;
148
+ command: string;
149
+ textToType: string;
150
+ startMarker: string;
151
+ endMarkerPrefix: string;
152
+ }
package/ts_kvm/ocr.ts ADDED
@@ -0,0 +1,41 @@
1
+ import * as plugins from './plugins.js';
2
+ import type { IOcrEngine, IOcrResult } from './interfaces.js';
3
+
4
+ export interface IKvmMistralOcrEngineOptions {
5
+ apiKey?: string;
6
+ model?: string;
7
+ endpointUrl?: string;
8
+ transport?: plugins.ocr.ISmartAiMistralOcrTransport;
9
+ includeImageBase64?: boolean;
10
+ tableFormat?: plugins.ocr.TSmartAiMistralOcrTableFormat;
11
+ extractHeader?: boolean;
12
+ extractFooter?: boolean;
13
+ confidenceScoresGranularity?: plugins.ocr.TSmartAiMistralOcrConfidenceScoresGranularity;
14
+ }
15
+
16
+ export const createMistralKvmOcrEngine = (
17
+ options: IKvmMistralOcrEngineOptions = {}
18
+ ): IOcrEngine => {
19
+ const ocrEngine = plugins.ocr.createMistralOcrEngine({
20
+ ...options,
21
+ confidenceScoresGranularity: options.confidenceScoresGranularity ?? 'page',
22
+ });
23
+
24
+ return {
25
+ recognize: async (frame, recognizeOptions = {}): Promise<IOcrResult> => {
26
+ if (recognizeOptions.crop) {
27
+ throw new Error('Mistral KVM OCR does not support crop options yet.');
28
+ }
29
+
30
+ const result = await ocrEngine.recognizeImage({
31
+ dataBase64: frame.dataBase64,
32
+ mimeType: frame.mimeType,
33
+ });
34
+
35
+ return {
36
+ text: result.text,
37
+ confidence: result.confidence,
38
+ };
39
+ },
40
+ };
41
+ };
@@ -0,0 +1,13 @@
1
+ // node native scope
2
+ import * as crypto from 'node:crypto';
3
+
4
+ export { crypto };
5
+
6
+ // third party scope
7
+ import puppeteer from 'puppeteer';
8
+
9
+ export { puppeteer };
10
+
11
+ // FlexHarness OCR
12
+ import * as ocr from '@modelprofile.com/flexharness/ocr';
13
+ export { ocr };
@@ -0,0 +1,482 @@
1
+ Programmable visual KVM automation for browser-based KVM devices.
2
+
3
+ `@modelprofile.com/flexharness/kvm` turns a remote machine that only exposes a visual KVM UI into a clean TypeScript control surface: capture frames in, send keyboard events out, and optionally run terminal commands through OCR-readable command wrappers.
4
+
5
+ Its core interfaces are transport-focused and model-agnostic, while the package also ships a ready-to-use Mistral OCR adapter via `@modelprofile.com/flexharness/ocr` for teams that want a working OCR path out of the box.
6
+
7
+ ## Issue Reporting and Security
8
+
9
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
10
+
11
+ ## What It Does
12
+
13
+ Many machines behind visual KVMs do not expose SSH, RDP, WinRM, a PTY, or any native management plane. The only reliable automation channel is often this:
14
+
15
+ ```txt
16
+ video feed in
17
+ keyboard events out
18
+ ```
19
+
20
+ `flexharness/kvm` makes that channel programmable.
21
+
22
+ The current driver opens the KVM web UI with Puppeteer, focuses the viewer, captures frames from a `video`, `canvas`, or viewer wrapper, and sends keyboard input through Chromium. The public interfaces are generic, so future drivers can target JetKVM native WebRTC/data channels, PiKVM-style APIs, TinyPilot, GL.iNet Comet, or HDMI capture plus USB HID without changing consumer code.
23
+
24
+ ## Highlights
25
+
26
+ - 🚀 `BrowserKvm` for Puppeteer-powered browser KVM automation.
27
+ - 📸 Frame capture as base64 PNG from `video`, `canvas`, or element screenshots.
28
+ - ⌨️ Keyboard transport with text typing, individual keys, and shortcuts.
29
+ - 🧠 Pluggable OCR interface plus a Mistral OCR adapter powered by `@modelprofile.com/flexharness/ocr`.
30
+ - 🧪 Terminal command wrappers with start/end markers and exit code parsing.
31
+ - 🧰 Minimal FlexHarness-compatible tools without importing `@modelprofile.com/flexharness-agent`.
32
+ - 🔌 Generic `IKvmDriver` abstraction ready for non-Puppeteer drivers later.
33
+
34
+ ## Install
35
+
36
+ ```sh
37
+ pnpm add @modelprofile.com/flexharness puppeteer
38
+ ```
39
+
40
+ ## Mental Model
41
+
42
+ `flexharness/kvm` provides these building blocks:
43
+
44
+ ```txt
45
+ BrowserKvm
46
+ opens the KVM web UI, captures frames, sends keyboard events
47
+
48
+ KvmTerminal
49
+ uses any IKvmDriver plus any IOcrEngine to type wrapped commands and parse OCR text
50
+
51
+ createMistralKvmOcrEngine()
52
+ provides a ready IOcrEngine backed by Mistral Document AI OCR
53
+
54
+ createKvmTools()
55
+ exposes terminal actions as small tool objects for agent frameworks
56
+ ```
57
+
58
+ The package deliberately does not implement mouse automation APIs, native JetKVM/WebRTC, native PiKVM APIs, terminal region auto-detection, or keyboard layout detection. The core still accepts any `IOcrEngine`; the Mistral adapter is just the first bundled OCR implementation.
59
+
60
+ ## Quick Start: Browser KVM Transport
61
+
62
+ ```typescript
63
+ import { BrowserKvm } from '@modelprofile.com/flexharness/kvm';
64
+
65
+ const kvm = new BrowserKvm({
66
+ url: 'https://jetkvm.local',
67
+ kind: 'jetkvm',
68
+ username: 'admin',
69
+ password: 'admin',
70
+ headless: false,
71
+ ignoreHttpsErrors: true,
72
+ });
73
+
74
+ await kvm.connect();
75
+
76
+ await kvm.typeText('hello from flexharness');
77
+ await kvm.pressKey('Enter');
78
+
79
+ const frame = await kvm.captureFrame();
80
+ console.log(frame.mimeType, frame.width, frame.height, frame.dataBase64.slice(0, 32));
81
+
82
+ await kvm.disconnect();
83
+ ```
84
+
85
+ ## Quick Start: Terminal With Mistral OCR
86
+
87
+ ```typescript
88
+ import {
89
+ BrowserKvm,
90
+ KvmTerminal,
91
+ createMistralKvmOcrEngine,
92
+ } from '@modelprofile.com/flexharness/kvm';
93
+
94
+ const kvm = new BrowserKvm({
95
+ url: 'https://jetkvm.local',
96
+ kind: 'jetkvm',
97
+ headless: false,
98
+ ignoreHttpsErrors: true,
99
+ });
100
+
101
+ await kvm.connect();
102
+
103
+ const terminal = new KvmTerminal({
104
+ kvm,
105
+ ocrEngine: createMistralKvmOcrEngine({
106
+ apiKey: process.env.MISTRAL_API_KEY,
107
+ }),
108
+ osHint: 'linux',
109
+ shellHint: 'bash',
110
+ });
111
+
112
+ const result = await terminal.runCommand('uname -a');
113
+ console.log(result.combinedText);
114
+
115
+ await kvm.disconnect();
116
+ ```
117
+
118
+ ## Quick Start: Terminal Commands Through OCR
119
+
120
+ ```typescript
121
+ import {
122
+ BrowserKvm,
123
+ KvmTerminal,
124
+ type IOcrEngine,
125
+ } from '@modelprofile.com/flexharness/kvm';
126
+
127
+ const ocrEngine: IOcrEngine = {
128
+ async recognize(frame, options) {
129
+ // Plug in your OCR engine here, for example Tesseract, a local OCR service,
130
+ // a screenshot OCR pipeline, or any implementation that returns text.
131
+ return {
132
+ text: '',
133
+ confidence: 0,
134
+ };
135
+ },
136
+ };
137
+
138
+ const kvm = new BrowserKvm({
139
+ url: 'https://some-kvm.local',
140
+ kind: 'generic',
141
+ headless: false,
142
+ ignoreHttpsErrors: true,
143
+ });
144
+
145
+ await kvm.connect();
146
+
147
+ const terminal = new KvmTerminal({
148
+ kvm,
149
+ ocrEngine,
150
+ osHint: 'linux',
151
+ shellHint: 'bash',
152
+ commandTimeoutMs: 30_000,
153
+ ocrPollIntervalMs: 500,
154
+ });
155
+
156
+ await terminal.bootstrap();
157
+
158
+ const result = await terminal.runCommand('pwd');
159
+
160
+ if (result.completed) {
161
+ console.log('exit:', result.exitCode);
162
+ console.log(result.combinedText);
163
+ } else {
164
+ console.log('command timed out');
165
+ console.log(result.rawOcrText);
166
+ }
167
+
168
+ await kvm.disconnect();
169
+ ```
170
+
171
+ ## BrowserKvm
172
+
173
+ `BrowserKvm` implements `IKvmDriver` with Puppeteer.
174
+
175
+ ```typescript
176
+ const kvm = new BrowserKvm({
177
+ url: 'https://kvm.local',
178
+ kind: 'jetkvm',
179
+ username: 'admin',
180
+ password: 'admin',
181
+ headless: false,
182
+ viewerSelector: 'video, canvas',
183
+ captureSelector: 'video, canvas',
184
+ ignoreHttpsErrors: true,
185
+ userDataDir: '.nogit/kvm-profile',
186
+ executablePath: '/usr/bin/chromium',
187
+ timeoutMs: 30_000,
188
+ });
189
+ ```
190
+
191
+ ### Browser Options
192
+
193
+ - `url`: the KVM web UI URL.
194
+ - `kind`: optional device hint, one of `jetkvm`, `glinet`, `pikvm`, `tinypilot`, or `generic`.
195
+ - `username` and `password`: optional credentials for generic login forms.
196
+ - `headless`: Puppeteer headless mode, defaults to `true`.
197
+ - `viewerSelector`: element that receives keyboard focus, defaults to `video, canvas`.
198
+ - `captureSelector`: element used for capture, defaults to `viewerSelector` and then `video, canvas`.
199
+ - `ignoreHttpsErrors`: accepts self-signed KVM certificates through Puppeteer `acceptInsecureCerts`.
200
+ - `userDataDir`: persists cookies and login state.
201
+ - `executablePath`: uses a specific Chromium or Chrome binary.
202
+ - `timeoutMs`: initial page load and viewer detection timeout, defaults to `30000`.
203
+
204
+ ### Connection Flow
205
+
206
+ `connect()` launches Chromium, opens `url`, attempts a generic login when credentials are supplied, waits for the viewer to become ready, and clicks the viewer once to focus it.
207
+
208
+ Generic login looks for common username and password fields, including `input[name="username"]`, `input[autocomplete="username"]`, `input[type="email"]`, `input[type="text"]`, `input[name="password"]`, and `input[type="password"]`. If matching fields are not found, login is skipped silently.
209
+
210
+ Viewer readiness accepts direct `video`, direct `canvas`, a wrapper containing either, or a generic visible wrapper with a non-zero bounding box. Video readiness requires `videoWidth` and `videoHeight`; canvas readiness requires `width` and `height`.
211
+
212
+ ### Frame Capture
213
+
214
+ `captureFrame()` returns an `IKvmFrame`:
215
+
216
+ ```typescript
217
+ interface IKvmFrame {
218
+ timestamp: number;
219
+ width: number;
220
+ height: number;
221
+ mimeType: 'image/png' | 'image/jpeg';
222
+ dataBase64: string;
223
+ }
224
+ ```
225
+
226
+ Capture strategy:
227
+
228
+ - Draw a selected `video` to an internal canvas and return PNG base64.
229
+ - Draw a selected `canvas` to another canvas and return PNG base64.
230
+ - If the selected element is a wrapper, capture its inner `video` or `canvas`.
231
+ - If no media element is present, fall back to a Puppeteer element screenshot.
232
+
233
+ ### Keyboard Control
234
+
235
+ ```typescript
236
+ await kvm.typeText('whoami', { delayMs: 10 });
237
+ await kvm.pressKey('Enter');
238
+ await kvm.pressShortcut(['Control', 'Alt', 'T']);
239
+ ```
240
+
241
+ Every keyboard method focuses the viewer first. `pressShortcut()` presses keys in order and releases them in reverse order.
242
+
243
+ There is intentionally no public mouse automation API in v1. The only mouse action is the internal click used to focus the viewer.
244
+
245
+ ## KvmTerminal
246
+
247
+ `KvmTerminal` uses any `IKvmDriver` and any `IOcrEngine` to type wrapped terminal commands and parse OCR text into command results.
248
+
249
+ ```typescript
250
+ const terminal = new KvmTerminal({
251
+ kvm,
252
+ ocrEngine,
253
+ osHint: 'windows',
254
+ shellHint: 'powershell',
255
+ commandTimeoutMs: 45_000,
256
+ ocrPollIntervalMs: 500,
257
+ ocrMaxAttempts: 120,
258
+ ocrCrop: {
259
+ x: 0,
260
+ y: 120,
261
+ width: 1280,
262
+ height: 600,
263
+ },
264
+ });
265
+ ```
266
+
267
+ ### Bootstrap Shortcuts
268
+
269
+ `bootstrap()` can open a terminal using keyboard-only defaults:
270
+
271
+ - `windows`: `Meta + R`, types `powershell -NoLogo`, then presses `Enter`.
272
+ - `macos`: `Meta + Space`, types `Terminal`, then presses `Enter`.
273
+ - `linux`: `Control + Alt + T`.
274
+ - `unknown`: does nothing.
275
+
276
+ Browser-based KVMs can intercept or remap shortcuts. `bootstrap()` intentionally implements only the generic path.
277
+
278
+ ### Running Commands
279
+
280
+ `runCommand(command)` creates a wrapped command, types it, presses `Enter`, then polls OCR until the end marker appears, the timeout is reached, or `ocrMaxAttempts` is reached.
281
+
282
+ ```typescript
283
+ const result = await terminal.runCommand('uname -a');
284
+
285
+ console.log(result.commandId);
286
+ console.log(result.completed);
287
+ console.log(result.timedOut);
288
+ console.log(result.exitCode);
289
+ console.log(result.combinedText);
290
+ console.log(result.rawOcrText);
291
+ ```
292
+
293
+ Command timeouts do not throw. They return:
294
+
295
+ ```typescript
296
+ {
297
+ completed: false,
298
+ timedOut: true,
299
+ combinedText: rawOcrText,
300
+ rawOcrText,
301
+ }
302
+ ```
303
+
304
+ Infrastructure failures still throw, for example an unconnected KVM, a missing viewer selector, a missing capture selector, or a media element without a frame.
305
+
306
+ ### Observing Text
307
+
308
+ `observeText()` captures one frame and sends it to the configured OCR engine:
309
+
310
+ ```typescript
311
+ const visibleText = await terminal.observeText();
312
+ ```
313
+
314
+ OCR is called with `{ language: 'eng', crop: options.ocrCrop }`.
315
+
316
+ ## Mistral OCR Adapter
317
+
318
+ `createMistralKvmOcrEngine()` returns an `IOcrEngine` backed by `@modelprofile.com/flexharness/ocr`, which uses Mistral Document AI OCR with `mistral-ocr-latest` by default.
319
+
320
+ ```typescript
321
+ import { createMistralKvmOcrEngine } from '@modelprofile.com/flexharness/kvm';
322
+
323
+ const ocrEngine = createMistralKvmOcrEngine({
324
+ apiKey: process.env.MISTRAL_API_KEY,
325
+ confidenceScoresGranularity: 'page',
326
+ });
327
+ ```
328
+
329
+ Options:
330
+
331
+ - `apiKey`: Mistral API key, required unless `transport` is supplied.
332
+ - `model`: OCR model, defaults to `mistral-ocr-latest`.
333
+ - `endpointUrl`: override the Mistral OCR endpoint.
334
+ - `confidenceScoresGranularity`: `'page'` | `'word'`, defaults to `'page'` in the KVM adapter.
335
+ - `tableFormat`: `'markdown'` | `'html'`.
336
+ - `extractHeader` and `extractFooter`: pass-through Mistral OCR flags.
337
+ - `transport`: test/custom transport hook inherited from `@modelprofile.com/flexharness/ocr`.
338
+
339
+ Current limitation: `IOcrRecognizeOptions.crop` is rejected by this adapter because the Mistral endpoint receives the full KVM frame. Use a separate image-cropping OCR engine if you need terminal-region cropping today.
340
+
341
+ ## Command Wrappers
342
+
343
+ The wrapper utilities make terminal output parseable through OCR by adding simple markers:
344
+
345
+ ```txt
346
+ SMARTKVM_START_<commandId>
347
+ SMARTKVM_END_<commandId>_<exitCode>
348
+ ```
349
+
350
+ ```typescript
351
+ import {
352
+ createWrappedKvmCommand,
353
+ parseWrappedKvmCommandOutput,
354
+ } from '@modelprofile.com/flexharness/kvm';
355
+
356
+ const wrapped = createWrappedKvmCommand('echo hello', 'bash');
357
+
358
+ console.log(wrapped.textToType);
359
+
360
+ const parsed = parseWrappedKvmCommandOutput({
361
+ commandId: wrapped.commandId,
362
+ startMarker: wrapped.startMarker,
363
+ endMarkerPrefix: wrapped.endMarkerPrefix,
364
+ rawText: `
365
+ prompt
366
+ ${wrapped.startMarker}
367
+ hello
368
+ ${wrapped.endMarkerPrefix}0
369
+ prompt
370
+ `,
371
+ });
372
+
373
+ console.log(parsed.completed); // true
374
+ console.log(parsed.exitCode); // 0
375
+ console.log(parsed.combinedText); // hello
376
+ ```
377
+
378
+ Supported shell hints are `bash`, `zsh`, `sh`, `powershell`, `cmd`, and `unknown`. `unknown` uses the POSIX-style wrapper.
379
+
380
+ The parser is intentionally simple and deterministic. It tolerates whitespace and line endings, but it does not do fuzzy OCR correction in v1.
381
+
382
+ ## FlexHarness Agent Tools
383
+
384
+ `createKvmTools()` returns small tool objects without importing `@modelprofile.com/flexharness-agent`.
385
+
386
+ ```typescript
387
+ import { createKvmTools } from '@modelprofile.com/flexharness/kvm';
388
+
389
+ const tools = createKvmTools({ terminal });
390
+
391
+ const runCommandTool = tools.find((tool) => tool.name === 'kvm_terminal_run_command');
392
+ const observeTool = tools.find((tool) => tool.name === 'kvm_terminal_observe');
393
+
394
+ const commandResult = await runCommandTool?.execute({ command: 'hostname' });
395
+ const currentText = await observeTool?.execute({});
396
+ ```
397
+
398
+ Included tools:
399
+
400
+ - `kvm_terminal_run_command`: accepts `{ command: string }` and returns `IKvmTerminalCommandResult`.
401
+ - `kvm_terminal_observe`: accepts `{}` and returns OCR text from the current frame.
402
+
403
+ ## Public API
404
+
405
+ Root exports:
406
+
407
+ ```typescript
408
+ export * from './interfaces.js';
409
+ export * from './classes.browserkvm.js';
410
+ export * from './classes.kvmterminal.js';
411
+ export * from './commandwrappers.js';
412
+ export * from './tools.js';
413
+ export * from './ocr.js';
414
+ ```
415
+
416
+ Important types:
417
+
418
+ - `IKvmDriver`: generic transport interface for connect, disconnect, focus, capture, typing, key presses, shortcuts, and wait.
419
+ - `IKvmFrame`: timestamped base64 frame payload.
420
+ - `IOcrEngine`: pluggable OCR contract.
421
+ - `IKvmTerminalOptions`: terminal transport, OCR, OS hint, shell hint, timeout, polling, attempts, and crop options.
422
+ - `IKvmTerminalCommandResult`: parsed command result with markers, completion state, timeout state, exit code, combined output, and raw OCR text.
423
+ - `IWrappedKvmCommand`: generated command wrapper metadata and typed command string.
424
+ - `IKvmTool`: minimal tool shape for agent integrations.
425
+ - `IKvmMistralOcrEngineOptions`: options for the bundled Mistral OCR adapter.
426
+
427
+ ## Driver Scope
428
+
429
+ The public abstraction is intentionally broader than Puppeteer. `BrowserKvm` is the first concrete driver, but consumers should depend on `IKvmDriver` when possible.
430
+
431
+ Future drivers can implement the same interface for:
432
+
433
+ - JetKVM native WebRTC or data-channel control.
434
+ - GL.iNet Comet and PiKVM-compatible APIs.
435
+ - TinyPilot.
436
+ - Custom HDMI capture plus USB HID control.
437
+
438
+ ## Manual Browser Test
439
+
440
+ Automated tests do not require real KVM hardware. The browser smoke test only runs when `FLEXHARNESS_KVM_TEST_URL` is set.
441
+
442
+ ```sh
443
+ FLEXHARNESS_KVM_TEST_URL=https://your-kvm.local pnpm exec tstest test_integration/test.kvm.browser.node.ts
444
+ ```
445
+
446
+ Optional variables:
447
+
448
+ - `FLEXHARNESS_KVM_TEST_USERNAME`
449
+ - `FLEXHARNESS_KVM_TEST_PASSWORD`
450
+ - `FLEXHARNESS_KVM_TEST_HEADLESS=false`
451
+
452
+ ## Development
453
+
454
+ ```sh
455
+ pnpm install
456
+ pnpm test
457
+ pnpm run build
458
+ tsbuild check "test/**/*"
459
+ ```
460
+
461
+ The package uses ESM, TypeScript, Puppeteer, and the push.rocks test/build stack.
462
+
463
+ ## License and Legal Information
464
+
465
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license.md) file.
466
+
467
+ **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
468
+
469
+ ### Trademarks
470
+
471
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
472
+
473
+ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
474
+
475
+ ### Company Information
476
+
477
+ Task Venture Capital GmbH
478
+ Registered at District Court Bremen HRB 35230 HB, Germany
479
+
480
+ For any legal inquiries or further information, please contact us via email at hello@task.vc.
481
+
482
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.