@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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts_chat/classes.chatsession.d.ts +37 -0
- package/dist_ts_chat/classes.chatsession.js +102 -0
- package/dist_ts_chat/index.d.ts +2 -0
- package/dist_ts_chat/index.js +2 -0
- package/dist_ts_chat/interfaces.d.ts +72 -0
- package/dist_ts_chat/interfaces.js +2 -0
- package/dist_ts_chat/plugins.d.ts +4 -0
- package/dist_ts_chat/plugins.js +4 -0
- package/dist_ts_chat_cli/components.chatapp.d.ts +10 -0
- package/dist_ts_chat_cli/components.chatapp.js +75 -0
- package/dist_ts_chat_cli/components.inputarea.d.ts +7 -0
- package/dist_ts_chat_cli/components.inputarea.js +18 -0
- package/dist_ts_chat_cli/components.message.d.ts +12 -0
- package/dist_ts_chat_cli/components.message.js +21 -0
- package/dist_ts_chat_cli/components.messagelist.d.ts +9 -0
- package/dist_ts_chat_cli/components.messagelist.js +19 -0
- package/dist_ts_chat_cli/components.statusbar.d.ts +9 -0
- package/dist_ts_chat_cli/components.statusbar.js +15 -0
- package/dist_ts_chat_cli/index.d.ts +2 -0
- package/dist_ts_chat_cli/index.js +2 -0
- package/dist_ts_chat_cli/plugins.d.ts +12 -0
- package/dist_ts_chat_cli/plugins.js +14 -0
- package/dist_ts_chat_cli/startchat.d.ts +18 -0
- package/dist_ts_chat_cli/startchat.js +17 -0
- package/dist_ts_chat_web/flexchat-input.d.ts +21 -0
- package/dist_ts_chat_web/flexchat-input.js +172 -0
- package/dist_ts_chat_web/flexchat-message.d.ts +26 -0
- package/dist_ts_chat_web/flexchat-message.js +174 -0
- package/dist_ts_chat_web/flexchat-window.d.ts +34 -0
- package/dist_ts_chat_web/flexchat-window.js +394 -0
- package/dist_ts_chat_web/index.d.ts +3 -0
- package/dist_ts_chat_web/index.js +4 -0
- package/dist_ts_chat_web/plugins.d.ts +6 -0
- package/dist_ts_chat_web/plugins.js +4 -0
- package/dist_ts_compaction/index.d.ts +2 -0
- package/dist_ts_compaction/index.js +2 -0
- package/dist_ts_compaction/plugins.d.ts +4 -0
- package/dist_ts_compaction/plugins.js +3 -0
- package/dist_ts_compaction/smartagent.compaction.d.ts +14 -0
- package/dist_ts_compaction/smartagent.compaction.js +49 -0
- package/dist_ts_document/index.d.ts +19 -0
- package/dist_ts_document/index.js +55 -0
- package/dist_ts_document/plugins.d.ts +3 -0
- package/dist_ts_document/plugins.js +4 -0
- package/dist_ts_kvm/classes.browserkvm.d.ts +22 -0
- package/dist_ts_kvm/classes.browserkvm.js +271 -0
- package/dist_ts_kvm/classes.kvmterminal.d.ts +8 -0
- package/dist_ts_kvm/classes.kvmterminal.js +88 -0
- package/dist_ts_kvm/commandwrappers.d.ts +14 -0
- package/dist_ts_kvm/commandwrappers.js +58 -0
- package/dist_ts_kvm/index.d.ts +6 -0
- package/dist_ts_kvm/index.js +7 -0
- package/dist_ts_kvm/interfaces.d.ts +106 -0
- package/dist_ts_kvm/interfaces.js +2 -0
- package/dist_ts_kvm/ocr.d.ts +14 -0
- package/dist_ts_kvm/ocr.js +23 -0
- package/dist_ts_kvm/plugins.d.ts +6 -0
- package/dist_ts_kvm/plugins.js +10 -0
- package/dist_ts_kvm/tools.d.ts +15 -0
- package/dist_ts_kvm/tools.js +40 -0
- package/dist_ts_mcp/index.d.ts +86 -0
- package/dist_ts_mcp/index.js +284 -0
- package/dist_ts_mcp/requestcontext.d.ts +11 -0
- package/dist_ts_mcp/requestcontext.js +42 -0
- package/dist_ts_media/index.d.ts +8 -0
- package/dist_ts_media/index.js +21 -0
- package/dist_ts_media/plugins.d.ts +2 -0
- package/dist_ts_media/plugins.js +3 -0
- package/dist_ts_ocr/classes.imageocr.d.ts +19 -0
- package/dist_ts_ocr/classes.imageocr.js +27 -0
- package/dist_ts_ocr/index.d.ts +2 -0
- package/dist_ts_ocr/index.js +3 -0
- package/dist_ts_ocr/ocr.mistral.d.ts +75 -0
- package/dist_ts_ocr/ocr.mistral.js +77 -0
- package/dist_ts_tools/index.d.ts +17 -0
- package/dist_ts_tools/index.js +10 -0
- package/dist_ts_tools/plugins.d.ts +7 -0
- package/dist_ts_tools/plugins.js +6 -0
- package/dist_ts_tools/tool.browser.d.ts +11 -0
- package/dist_ts_tools/tool.browser.js +128 -0
- package/dist_ts_tools/tool.context.d.ts +4 -0
- package/dist_ts_tools/tool.context.js +23 -0
- package/dist_ts_tools/tool.filesystem.d.ts +11 -0
- package/dist_ts_tools/tool.filesystem.js +106 -0
- package/dist_ts_tools/tool.http.d.ts +2 -0
- package/dist_ts_tools/tool.http.js +65 -0
- package/dist_ts_tools/tool.json.d.ts +2 -0
- package/dist_ts_tools/tool.json.js +47 -0
- package/dist_ts_tools/tool.shell.d.ts +15 -0
- package/dist_ts_tools/tool.shell.js +329 -0
- package/dist_ts_tools/utils.truncation.d.ts +10 -0
- package/dist_ts_tools/utils.truncation.js +26 -0
- package/dist_ts_tools_node/index.d.ts +5 -0
- package/dist_ts_tools_node/index.js +4 -0
- package/dist_ts_tools_node/plugins.d.ts +13 -0
- package/dist_ts_tools_node/plugins.js +13 -0
- package/dist_ts_tools_node/tool.context.d.ts +31 -0
- package/dist_ts_tools_node/tool.context.js +837 -0
- package/dist_ts_tools_node/tool.persistence.d.ts +15 -0
- package/dist_ts_tools_node/tool.persistence.js +129 -0
- package/dist_ts_tools_node/tools.local.d.ts +11 -0
- package/dist_ts_tools_node/tools.local.js +15 -0
- package/package.json +112 -4
- package/readme.md +122 -66
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/readme.md +122 -66
- package/ts_chat/classes.chatsession.ts +116 -0
- package/ts_chat/index.ts +8 -0
- package/ts_chat/interfaces.ts +64 -0
- package/ts_chat/plugins.ts +7 -0
- package/ts_chat/readme.md +49 -0
- package/ts_chat_cli/components.chatapp.ts +103 -0
- package/ts_chat_cli/components.inputarea.ts +29 -0
- package/ts_chat_cli/components.message.ts +48 -0
- package/ts_chat_cli/components.messagelist.ts +45 -0
- package/ts_chat_cli/components.statusbar.ts +31 -0
- package/ts_chat_cli/index.ts +2 -0
- package/ts_chat_cli/plugins.ts +21 -0
- package/ts_chat_cli/readme.md +40 -0
- package/ts_chat_cli/startchat.ts +34 -0
- package/ts_chat_web/flexchat-input.ts +184 -0
- package/ts_chat_web/flexchat-message.ts +185 -0
- package/ts_chat_web/flexchat-window.ts +416 -0
- package/ts_chat_web/index.ts +3 -0
- package/ts_chat_web/plugins.ts +9 -0
- package/ts_chat_web/readme.md +46 -0
- package/ts_compaction/index.ts +2 -0
- package/ts_compaction/plugins.ts +6 -0
- package/ts_compaction/readme.md +37 -0
- package/ts_compaction/smartagent.compaction.ts +59 -0
- package/ts_document/index.ts +82 -0
- package/ts_document/plugins.ts +3 -0
- package/ts_document/readme.md +49 -0
- package/ts_kvm/classes.browserkvm.ts +335 -0
- package/ts_kvm/classes.kvmterminal.ts +103 -0
- package/ts_kvm/commandwrappers.ts +83 -0
- package/ts_kvm/index.ts +6 -0
- package/ts_kvm/interfaces.ts +152 -0
- package/ts_kvm/ocr.ts +41 -0
- package/ts_kvm/plugins.ts +13 -0
- package/ts_kvm/readme.md +482 -0
- package/ts_kvm/tools.ts +58 -0
- package/ts_mcp/index.ts +470 -0
- package/ts_mcp/readme.md +37 -0
- package/ts_mcp/requestcontext.ts +66 -0
- package/ts_media/index.ts +29 -0
- package/ts_media/plugins.ts +2 -0
- package/ts_media/readme.md +37 -0
- package/ts_ocr/classes.imageocr.ts +53 -0
- package/ts_ocr/index.ts +2 -0
- package/ts_ocr/ocr.mistral.ts +192 -0
- package/ts_ocr/readme.md +48 -0
- package/ts_tools/index.ts +49 -0
- package/ts_tools/plugins.ts +7 -0
- package/ts_tools/readme.md +37 -0
- package/ts_tools/tool.browser.ts +159 -0
- package/ts_tools/tool.context.ts +23 -0
- package/ts_tools/tool.filesystem.ts +144 -0
- package/ts_tools/tool.http.ts +78 -0
- package/ts_tools/tool.json.ts +53 -0
- package/ts_tools/tool.shell.ts +418 -0
- package/ts_tools/utils.truncation.ts +39 -0
- package/ts_tools_node/index.ts +5 -0
- package/ts_tools_node/plugins.ts +16 -0
- package/ts_tools_node/readme.md +37 -0
- package/ts_tools_node/tool.context.ts +991 -0
- package/ts_tools_node/tool.persistence.ts +146 -0
- package/ts_tools_node/tools.local.ts +26 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
const defaultViewerSelector = 'video, canvas';
|
|
3
|
+
export class BrowserKvm {
|
|
4
|
+
kind;
|
|
5
|
+
options;
|
|
6
|
+
browser;
|
|
7
|
+
page;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
this.kind = options.kind ?? 'generic';
|
|
11
|
+
}
|
|
12
|
+
async connect() {
|
|
13
|
+
if (this.browser && this.page) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const timeoutMs = this.options.timeoutMs ?? 30000;
|
|
17
|
+
const args = [];
|
|
18
|
+
if (process.env.CI || process.getuid?.() === 0) {
|
|
19
|
+
args.push('--no-sandbox', '--disable-setuid-sandbox');
|
|
20
|
+
}
|
|
21
|
+
this.browser = await plugins.puppeteer.launch({
|
|
22
|
+
args,
|
|
23
|
+
acceptInsecureCerts: this.options.ignoreHttpsErrors ?? false,
|
|
24
|
+
defaultViewport: null,
|
|
25
|
+
executablePath: this.options.executablePath,
|
|
26
|
+
headless: this.options.headless ?? true,
|
|
27
|
+
timeout: timeoutMs,
|
|
28
|
+
userDataDir: this.options.userDataDir,
|
|
29
|
+
});
|
|
30
|
+
this.page = await this.browser.newPage();
|
|
31
|
+
this.page.setDefaultTimeout(timeoutMs);
|
|
32
|
+
this.page.setDefaultNavigationTimeout(timeoutMs);
|
|
33
|
+
await this.page.goto(this.options.url, {
|
|
34
|
+
waitUntil: 'domcontentloaded',
|
|
35
|
+
timeout: timeoutMs,
|
|
36
|
+
});
|
|
37
|
+
await this.tryGenericLogin();
|
|
38
|
+
await this.waitForViewerReady();
|
|
39
|
+
await this.focusViewer();
|
|
40
|
+
}
|
|
41
|
+
async disconnect() {
|
|
42
|
+
const browser = this.browser;
|
|
43
|
+
this.page = undefined;
|
|
44
|
+
this.browser = undefined;
|
|
45
|
+
if (browser) {
|
|
46
|
+
await browser.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async focusViewer() {
|
|
50
|
+
const page = this.requirePage();
|
|
51
|
+
const viewerSelector = this.getViewerSelector();
|
|
52
|
+
const viewerElement = await page.$(viewerSelector);
|
|
53
|
+
if (!viewerElement) {
|
|
54
|
+
throw new Error(`KVM viewer selector missing: ${viewerSelector}`);
|
|
55
|
+
}
|
|
56
|
+
await viewerElement.click();
|
|
57
|
+
}
|
|
58
|
+
async captureFrame() {
|
|
59
|
+
const page = this.requirePage();
|
|
60
|
+
const captureSelector = this.getCaptureSelector();
|
|
61
|
+
const captureElement = await page.$(captureSelector);
|
|
62
|
+
if (!captureElement) {
|
|
63
|
+
throw new Error(`KVM capture selector missing: ${captureSelector}`);
|
|
64
|
+
}
|
|
65
|
+
const mediaResult = await page.evaluate((selector) => {
|
|
66
|
+
const rootElement = document.querySelector(selector);
|
|
67
|
+
if (!rootElement) {
|
|
68
|
+
return {
|
|
69
|
+
captured: false,
|
|
70
|
+
error: `KVM capture selector missing: ${selector}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const mediaElement = (rootElement.matches('video, canvas')
|
|
74
|
+
? rootElement
|
|
75
|
+
: rootElement.querySelector('video, canvas'));
|
|
76
|
+
if (!mediaElement) {
|
|
77
|
+
return {
|
|
78
|
+
captured: false,
|
|
79
|
+
hasMediaElement: false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
let width = 0;
|
|
83
|
+
let height = 0;
|
|
84
|
+
if (mediaElement instanceof HTMLVideoElement) {
|
|
85
|
+
width = mediaElement.videoWidth;
|
|
86
|
+
height = mediaElement.videoHeight;
|
|
87
|
+
}
|
|
88
|
+
else if (mediaElement instanceof HTMLCanvasElement) {
|
|
89
|
+
width = mediaElement.width;
|
|
90
|
+
height = mediaElement.height;
|
|
91
|
+
}
|
|
92
|
+
if (width <= 0 || height <= 0) {
|
|
93
|
+
return {
|
|
94
|
+
captured: false,
|
|
95
|
+
hasMediaElement: true,
|
|
96
|
+
mediaHasFrame: false,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const canvasElement = document.createElement('canvas');
|
|
101
|
+
canvasElement.width = width;
|
|
102
|
+
canvasElement.height = height;
|
|
103
|
+
const context = canvasElement.getContext('2d');
|
|
104
|
+
if (!context) {
|
|
105
|
+
return {
|
|
106
|
+
captured: false,
|
|
107
|
+
hasMediaElement: true,
|
|
108
|
+
mediaHasFrame: true,
|
|
109
|
+
error: 'Could not create canvas 2D context for KVM frame capture.',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
context.drawImage(mediaElement, 0, 0, width, height);
|
|
113
|
+
const dataUrl = canvasElement.toDataURL('image/png');
|
|
114
|
+
return {
|
|
115
|
+
captured: true,
|
|
116
|
+
width,
|
|
117
|
+
height,
|
|
118
|
+
dataBase64: dataUrl.slice(dataUrl.indexOf(',') + 1),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
captured: false,
|
|
124
|
+
hasMediaElement: true,
|
|
125
|
+
mediaHasFrame: true,
|
|
126
|
+
error: error instanceof Error ? error.message : String(error),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}, captureSelector);
|
|
130
|
+
if (mediaResult.captured) {
|
|
131
|
+
return {
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
width: mediaResult.width ?? 0,
|
|
134
|
+
height: mediaResult.height ?? 0,
|
|
135
|
+
mimeType: 'image/png',
|
|
136
|
+
dataBase64: mediaResult.dataBase64 ?? '',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (mediaResult.error?.startsWith('KVM capture selector missing')) {
|
|
140
|
+
throw new Error(mediaResult.error);
|
|
141
|
+
}
|
|
142
|
+
if (mediaResult.hasMediaElement && mediaResult.mediaHasFrame === false) {
|
|
143
|
+
throw new Error(`KVM media element has no frame: ${captureSelector}`);
|
|
144
|
+
}
|
|
145
|
+
const boundingBox = await captureElement.boundingBox();
|
|
146
|
+
if (!boundingBox || boundingBox.width <= 0 || boundingBox.height <= 0) {
|
|
147
|
+
throw new Error(`KVM capture selector has no visible frame: ${captureSelector}`);
|
|
148
|
+
}
|
|
149
|
+
const screenshot = await captureElement.screenshot({
|
|
150
|
+
type: 'png',
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
timestamp: Date.now(),
|
|
154
|
+
width: Math.round(boundingBox.width),
|
|
155
|
+
height: Math.round(boundingBox.height),
|
|
156
|
+
mimeType: 'image/png',
|
|
157
|
+
dataBase64: Buffer.from(screenshot).toString('base64'),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async typeText(text, options) {
|
|
161
|
+
const page = this.requirePage();
|
|
162
|
+
await this.focusViewer();
|
|
163
|
+
await page.keyboard.type(text, {
|
|
164
|
+
delay: options?.delayMs,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
async pressKey(key) {
|
|
168
|
+
const page = this.requirePage();
|
|
169
|
+
await this.focusViewer();
|
|
170
|
+
await page.keyboard.press(key);
|
|
171
|
+
}
|
|
172
|
+
async pressShortcut(keys) {
|
|
173
|
+
const page = this.requirePage();
|
|
174
|
+
await this.focusViewer();
|
|
175
|
+
const pressedKeys = [];
|
|
176
|
+
try {
|
|
177
|
+
for (const key of keys) {
|
|
178
|
+
await page.keyboard.down(key);
|
|
179
|
+
pressedKeys.push(key);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
for (const key of pressedKeys.reverse()) {
|
|
184
|
+
await page.keyboard.up(key);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async wait(milliseconds) {
|
|
189
|
+
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
190
|
+
}
|
|
191
|
+
async tryGenericLogin() {
|
|
192
|
+
if (!this.options.username || !this.options.password) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const page = this.requirePage();
|
|
196
|
+
const usernameElement = await this.findFirstElement([
|
|
197
|
+
'input[name="username"]',
|
|
198
|
+
'input[autocomplete="username"]',
|
|
199
|
+
'input[type="email"]',
|
|
200
|
+
'input[type="text"]',
|
|
201
|
+
]);
|
|
202
|
+
const passwordElement = await this.findFirstElement([
|
|
203
|
+
'input[name="password"]',
|
|
204
|
+
'input[autocomplete="current-password"]',
|
|
205
|
+
'input[type="password"]',
|
|
206
|
+
]);
|
|
207
|
+
if (!usernameElement || !passwordElement) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
await usernameElement.click({ clickCount: 3 });
|
|
211
|
+
await usernameElement.type(this.options.username);
|
|
212
|
+
await passwordElement.click({ clickCount: 3 });
|
|
213
|
+
await passwordElement.type(this.options.password);
|
|
214
|
+
await passwordElement.press('Enter');
|
|
215
|
+
await Promise.race([
|
|
216
|
+
page
|
|
217
|
+
.waitForNavigation({
|
|
218
|
+
waitUntil: 'domcontentloaded',
|
|
219
|
+
timeout: Math.min(this.options.timeoutMs ?? 30000, 10000),
|
|
220
|
+
})
|
|
221
|
+
.catch(() => undefined),
|
|
222
|
+
this.wait(1000),
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
225
|
+
async waitForViewerReady() {
|
|
226
|
+
const page = this.requirePage();
|
|
227
|
+
const viewerSelector = this.getViewerSelector();
|
|
228
|
+
await page.waitForFunction((selector) => {
|
|
229
|
+
const rootElement = document.querySelector(selector);
|
|
230
|
+
if (!rootElement) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
const mediaElement = (rootElement.matches('video, canvas')
|
|
234
|
+
? rootElement
|
|
235
|
+
: rootElement.querySelector('video, canvas'));
|
|
236
|
+
if (mediaElement instanceof HTMLVideoElement) {
|
|
237
|
+
return mediaElement.videoWidth > 0 && mediaElement.videoHeight > 0;
|
|
238
|
+
}
|
|
239
|
+
if (mediaElement instanceof HTMLCanvasElement) {
|
|
240
|
+
return mediaElement.width > 0 && mediaElement.height > 0;
|
|
241
|
+
}
|
|
242
|
+
const boundingRect = rootElement.getBoundingClientRect();
|
|
243
|
+
return boundingRect.width > 0 && boundingRect.height > 0;
|
|
244
|
+
}, {
|
|
245
|
+
timeout: this.options.timeoutMs ?? 30000,
|
|
246
|
+
}, viewerSelector);
|
|
247
|
+
}
|
|
248
|
+
async findFirstElement(selectors) {
|
|
249
|
+
const page = this.requirePage();
|
|
250
|
+
for (const selector of selectors) {
|
|
251
|
+
const element = await page.$(selector);
|
|
252
|
+
if (element) {
|
|
253
|
+
return element;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
getViewerSelector() {
|
|
259
|
+
return this.options.viewerSelector ?? defaultViewerSelector;
|
|
260
|
+
}
|
|
261
|
+
getCaptureSelector() {
|
|
262
|
+
return this.options.captureSelector ?? this.options.viewerSelector ?? defaultViewerSelector;
|
|
263
|
+
}
|
|
264
|
+
requirePage() {
|
|
265
|
+
if (!this.page) {
|
|
266
|
+
throw new Error('BrowserKvm is not connected. Call connect() first.');
|
|
267
|
+
}
|
|
268
|
+
return this.page;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5icm93c2Vya3ZtLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfa3ZtL2NsYXNzZXMuYnJvd3Nlcmt2bS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQVV4QyxNQUFNLHFCQUFxQixHQUFHLGVBQWUsQ0FBQztBQVk5QyxNQUFNLE9BQU8sVUFBVTtJQUNMLElBQUksQ0FBVztJQUV2QixPQUFPLENBQXFCO0lBQzVCLE9BQU8sQ0FBNkI7SUFDcEMsSUFBSSxDQUEwQjtJQUV0QyxZQUFZLE9BQTJCO1FBQ3JDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksSUFBSSxTQUFTLENBQUM7SUFDeEMsQ0FBQztJQUVNLEtBQUssQ0FBQyxPQUFPO1FBQ2xCLElBQUksSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDOUIsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsSUFBSSxLQUFLLENBQUM7UUFDbEQsTUFBTSxJQUFJLEdBQWEsRUFBRSxDQUFDO1FBQzFCLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLElBQUksT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDL0MsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsMEJBQTBCLENBQUMsQ0FBQztRQUN4RCxDQUFDO1FBRUQsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDO1lBQzVDLElBQUk7WUFDSixtQkFBbUIsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixJQUFJLEtBQUs7WUFDNUQsZUFBZSxFQUFFLElBQUk7WUFDckIsY0FBYyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYztZQUMzQyxRQUFRLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLElBQUksSUFBSTtZQUN2QyxPQUFPLEVBQUUsU0FBUztZQUNsQixXQUFXLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXO1NBQ3RDLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ3pDLElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDdkMsSUFBSSxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUVqRCxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFO1lBQ3JDLFNBQVMsRUFBRSxrQkFBa0I7WUFDN0IsT0FBTyxFQUFFLFNBQVM7U0FDbkIsQ0FBQyxDQUFDO1FBRUgsTUFBTSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7UUFDN0IsTUFBTSxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUNoQyxNQUFNLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRU0sS0FBSyxDQUFDLFVBQVU7UUFDckIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUM3QixJQUFJLENBQUMsSUFBSSxHQUFHLFNBQVMsQ0FBQztRQUN0QixJQUFJLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQztRQUN6QixJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osTUFBTSxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDeEIsQ0FBQztJQUNILENBQUM7SUFFTSxLQUFLLENBQUMsV0FBVztRQUN0QixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDaEMsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDaEQsTUFBTSxhQUFhLEdBQUcsTUFBTSxJQUFJLENBQUMsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ25ELElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUNuQixNQUFNLElBQUksS0FBSyxDQUFDLGdDQUFnQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO1FBQ3BFLENBQUM7UUFDRCxNQUFNLGFBQWEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBRU0sS0FBSyxDQUFDLFlBQVk7UUFDdkIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQ2xELE1BQU0sY0FBYyxHQUFHLE1BQU0sSUFBSSxDQUFDLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUNyRCxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDcEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsZUFBZSxFQUFFLENBQUMsQ0FBQztRQUN0RSxDQUFDO1FBRUQsTUFBTSxXQUFXLEdBQUcsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsUUFBZ0IsRUFBdUIsRUFBRTtZQUNoRixNQUFNLFdBQVcsR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ3JELElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDakIsT0FBTztvQkFDTCxRQUFRLEVBQUUsS0FBSztvQkFDZixLQUFLLEVBQUUsaUNBQWlDLFFBQVEsRUFBRTtpQkFDbkQsQ0FBQztZQUNKLENBQUM7WUFFRCxNQUFNLFlBQVksR0FBRyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDO2dCQUN4RCxDQUFDLENBQUMsV0FBVztnQkFDYixDQUFDLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxlQUFlLENBQUMsQ0FBZ0QsQ0FBQztZQUUvRixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7Z0JBQ2xCLE9BQU87b0JBQ0wsUUFBUSxFQUFFLEtBQUs7b0JBQ2YsZUFBZSxFQUFFLEtBQUs7aUJBQ3ZCLENBQUM7WUFDSixDQUFDO1lBRUQsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO1lBQ2QsSUFBSSxNQUFNLEdBQUcsQ0FBQyxDQUFDO1lBQ2YsSUFBSSxZQUFZLFlBQVksZ0JBQWdCLEVBQUUsQ0FBQztnQkFDN0MsS0FBSyxHQUFHLFlBQVksQ0FBQyxVQUFVLENBQUM7Z0JBQ2hDLE1BQU0sR0FBRyxZQUFZLENBQUMsV0FBVyxDQUFDO1lBQ3BDLENBQUM7aUJBQU0sSUFBSSxZQUFZLFlBQVksaUJBQWlCLEVBQUUsQ0FBQztnQkFDckQsS0FBSyxHQUFHLFlBQVksQ0FBQyxLQUFLLENBQUM7Z0JBQzNCLE1BQU0sR0FBRyxZQUFZLENBQUMsTUFBTSxDQUFDO1lBQy9CLENBQUM7WUFFRCxJQUFJLEtBQUssSUFBSSxDQUFDLElBQUksTUFBTSxJQUFJLENBQUMsRUFBRSxDQUFDO2dCQUM5QixPQUFPO29CQUNMLFFBQVEsRUFBRSxLQUFLO29CQUNmLGVBQWUsRUFBRSxJQUFJO29CQUNyQixhQUFhLEVBQUUsS0FBSztpQkFDckIsQ0FBQztZQUNKLENBQUM7WUFFRCxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxhQUFhLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQztnQkFDdkQsYUFBYSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7Z0JBQzVCLGFBQWEsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO2dCQUM5QixNQUFNLE9BQU8sR0FBRyxhQUFhLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUMvQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7b0JBQ2IsT0FBTzt3QkFDTCxRQUFRLEVBQUUsS0FBSzt3QkFDZixlQUFlLEVBQUUsSUFBSTt3QkFDckIsYUFBYSxFQUFFLElBQUk7d0JBQ25CLEtBQUssRUFBRSwyREFBMkQ7cUJBQ25FLENBQUM7Z0JBQ0osQ0FBQztnQkFDRCxPQUFPLENBQUMsU0FBUyxDQUFDLFlBQVksRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQztnQkFDckQsTUFBTSxPQUFPLEdBQUcsYUFBYSxDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDckQsT0FBTztvQkFDTCxRQUFRLEVBQUUsSUFBSTtvQkFDZCxLQUFLO29CQUNMLE1BQU07b0JBQ04sVUFBVSxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7aUJBQ3BELENBQUM7WUFDSixDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixPQUFPO29CQUNMLFFBQVEsRUFBRSxLQUFLO29CQUNmLGVBQWUsRUFBRSxJQUFJO29CQUNyQixhQUFhLEVBQUUsSUFBSTtvQkFDbkIsS0FBSyxFQUFFLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7aUJBQzlELENBQUM7WUFDSixDQUFDO1FBQ0gsQ0FBQyxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBRXBCLElBQUksV0FBVyxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3pCLE9BQU87Z0JBQ0wsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7Z0JBQ3JCLEtBQUssRUFBRSxXQUFXLENBQUMsS0FBSyxJQUFJLENBQUM7Z0JBQzdCLE1BQU0sRUFBRSxXQUFXLENBQUMsTUFBTSxJQUFJLENBQUM7Z0JBQy9CLFFBQVEsRUFBRSxXQUFXO2dCQUNyQixVQUFVLEVBQUUsV0FBVyxDQUFDLFVBQVUsSUFBSSxFQUFFO2FBQ3pDLENBQUM7UUFDSixDQUFDO1FBRUQsSUFBSSxXQUFXLENBQUMsS0FBSyxFQUFFLFVBQVUsQ0FBQyw4QkFBOEIsQ0FBQyxFQUFFLENBQUM7WUFDbEUsTUFBTSxJQUFJLEtBQUssQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDckMsQ0FBQztRQUVELElBQUksV0FBVyxDQUFDLGVBQWUsSUFBSSxXQUFXLENBQUMsYUFBYSxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ3ZFLE1BQU0sSUFBSSxLQUFLLENBQUMsbUNBQW1DLGVBQWUsRUFBRSxDQUFDLENBQUM7UUFDeEUsQ0FBQztRQUVELE1BQU0sV0FBVyxHQUFHLE1BQU0sY0FBYyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3ZELElBQUksQ0FBQyxXQUFXLElBQUksV0FBVyxDQUFDLEtBQUssSUFBSSxDQUFDLElBQUksV0FBVyxDQUFDLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUN0RSxNQUFNLElBQUksS0FBSyxDQUFDLDhDQUE4QyxlQUFlLEVBQUUsQ0FBQyxDQUFDO1FBQ25GLENBQUM7UUFFRCxNQUFNLFVBQVUsR0FBRyxNQUFNLGNBQWMsQ0FBQyxVQUFVLENBQUM7WUFDakQsSUFBSSxFQUFFLEtBQUs7U0FDWixDQUFDLENBQUM7UUFFSCxPQUFPO1lBQ0wsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDckIsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQztZQUNwQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDO1lBQ3RDLFFBQVEsRUFBRSxXQUFXO1lBQ3JCLFVBQVUsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUM7U0FDdkQsQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsUUFBUSxDQUFDLElBQVksRUFBRSxPQUE2QjtRQUMvRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDaEMsTUFBTSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDekIsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDN0IsS0FBSyxFQUFFLE9BQU8sRUFBRSxPQUFPO1NBQ3hCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTSxLQUFLLENBQUMsUUFBUSxDQUFDLEdBQVk7UUFDaEMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3pCLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsR0FBaUMsQ0FBQyxDQUFDO0lBQy9ELENBQUM7SUFFTSxLQUFLLENBQUMsYUFBYSxDQUFDLElBQWU7UUFDeEMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBRXpCLE1BQU0sV0FBVyxHQUFjLEVBQUUsQ0FBQztRQUNsQyxJQUFJLENBQUM7WUFDSCxLQUFLLE1BQU0sR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO2dCQUN2QixNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQWlDLENBQUMsQ0FBQztnQkFDNUQsV0FBVyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN4QixDQUFDO1FBQ0gsQ0FBQztnQkFBUyxDQUFDO1lBQ1QsS0FBSyxNQUFNLEdBQUcsSUFBSSxXQUFXLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztnQkFDeEMsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxHQUFpQyxDQUFDLENBQUM7WUFDNUQsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUksQ0FBQyxZQUFvQjtRQUNwQyxNQUFNLElBQUksT0FBTyxDQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7SUFDMUUsQ0FBQztJQUVPLEtBQUssQ0FBQyxlQUFlO1FBQzNCLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDckQsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDaEMsTUFBTSxlQUFlLEdBQUcsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUM7WUFDbEQsd0JBQXdCO1lBQ3hCLGdDQUFnQztZQUNoQyxxQkFBcUI7WUFDckIsb0JBQW9CO1NBQ3JCLENBQUMsQ0FBQztRQUNILE1BQU0sZUFBZSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDO1lBQ2xELHdCQUF3QjtZQUN4Qix3Q0FBd0M7WUFDeEMsd0JBQXdCO1NBQ3pCLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxlQUFlLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUN6QyxPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sZUFBZSxDQUFDLEtBQUssQ0FBQyxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQy9DLE1BQU0sZUFBZSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2xELE1BQU0sZUFBZSxDQUFDLEtBQUssQ0FBQyxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQy9DLE1BQU0sZUFBZSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2xELE1BQU0sZUFBZSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUVyQyxNQUFNLE9BQU8sQ0FBQyxJQUFJLENBQUM7WUFDakIsSUFBSTtpQkFDRCxpQkFBaUIsQ0FBQztnQkFDakIsU0FBUyxFQUFFLGtCQUFrQjtnQkFDN0IsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLElBQUksS0FBSyxFQUFFLEtBQUssQ0FBQzthQUMxRCxDQUFDO2lCQUNELEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxTQUFTLENBQUM7WUFDekIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7U0FDaEIsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVPLEtBQUssQ0FBQyxrQkFBa0I7UUFDOUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBQ2hELE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FDeEIsQ0FBQyxRQUFnQixFQUFFLEVBQUU7WUFDbkIsTUFBTSxXQUFXLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNyRCxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7Z0JBQ2pCLE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztZQUVELE1BQU0sWUFBWSxHQUFHLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUM7Z0JBQ3hELENBQUMsQ0FBQyxXQUFXO2dCQUNiLENBQUMsQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLGVBQWUsQ0FBQyxDQUFnRCxDQUFDO1lBRS9GLElBQUksWUFBWSxZQUFZLGdCQUFnQixFQUFFLENBQUM7Z0JBQzdDLE9BQU8sWUFBWSxDQUFDLFVBQVUsR0FBRyxDQUFDLElBQUksWUFBWSxDQUFDLFdBQVcsR0FBRyxDQUFDLENBQUM7WUFDckUsQ0FBQztZQUVELElBQUksWUFBWSxZQUFZLGlCQUFpQixFQUFFLENBQUM7Z0JBQzlDLE9BQU8sWUFBWSxDQUFDLEtBQUssR0FBRyxDQUFDLElBQUksWUFBWSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7WUFDM0QsQ0FBQztZQUVELE1BQU0sWUFBWSxHQUFHLFdBQVcsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1lBQ3pELE9BQU8sWUFBWSxDQUFDLEtBQUssR0FBRyxDQUFDLElBQUksWUFBWSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFDM0QsQ0FBQyxFQUNEO1lBQ0UsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxJQUFJLEtBQUs7U0FDekMsRUFDRCxjQUFjLENBQ2YsQ0FBQztJQUNKLENBQUM7SUFFTyxLQUFLLENBQUMsZ0JBQWdCLENBQzVCLFNBQW1CO1FBRW5CLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNoQyxLQUFLLE1BQU0sUUFBUSxJQUFJLFNBQVMsRUFBRSxDQUFDO1lBQ2pDLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUN2QyxJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUNaLE9BQU8sT0FBTyxDQUFDO1lBQ2pCLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRU8saUJBQWlCO1FBQ3ZCLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLElBQUkscUJBQXFCLENBQUM7SUFDOUQsQ0FBQztJQUVPLGtCQUFrQjtRQUN4QixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxJQUFJLHFCQUFxQixDQUFDO0lBQzlGLENBQUM7SUFFTyxXQUFXO1FBQ2pCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDZixNQUFNLElBQUksS0FBSyxDQUFDLG9EQUFvRCxDQUFDLENBQUM7UUFDeEUsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQztJQUNuQixDQUFDO0NBQ0YifQ==
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IKvmTerminalCommandResult, IKvmTerminalOptions } from './interfaces.js';
|
|
2
|
+
export declare class KvmTerminal {
|
|
3
|
+
private options;
|
|
4
|
+
constructor(options: IKvmTerminalOptions);
|
|
5
|
+
bootstrap(): Promise<void>;
|
|
6
|
+
runCommand(command: string): Promise<IKvmTerminalCommandResult>;
|
|
7
|
+
observeText(): Promise<string>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createWrappedKvmCommand, parseWrappedKvmCommandOutput } from './commandwrappers.js';
|
|
2
|
+
export class KvmTerminal {
|
|
3
|
+
options;
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
}
|
|
7
|
+
async bootstrap() {
|
|
8
|
+
const osHint = this.options.osHint ?? 'unknown';
|
|
9
|
+
switch (osHint) {
|
|
10
|
+
case 'windows':
|
|
11
|
+
await this.options.kvm.pressShortcut(['Meta', 'R']);
|
|
12
|
+
await this.options.kvm.wait(500);
|
|
13
|
+
await this.options.kvm.typeText('powershell -NoLogo');
|
|
14
|
+
await this.options.kvm.pressKey('Enter');
|
|
15
|
+
await this.options.kvm.wait(1500);
|
|
16
|
+
break;
|
|
17
|
+
case 'macos':
|
|
18
|
+
await this.options.kvm.pressShortcut(['Meta', 'Space']);
|
|
19
|
+
await this.options.kvm.wait(500);
|
|
20
|
+
await this.options.kvm.typeText('Terminal');
|
|
21
|
+
await this.options.kvm.pressKey('Enter');
|
|
22
|
+
await this.options.kvm.wait(1500);
|
|
23
|
+
break;
|
|
24
|
+
case 'linux':
|
|
25
|
+
await this.options.kvm.pressShortcut(['Control', 'Alt', 'T']);
|
|
26
|
+
await this.options.kvm.wait(1500);
|
|
27
|
+
break;
|
|
28
|
+
case 'unknown':
|
|
29
|
+
default:
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async runCommand(command) {
|
|
34
|
+
const wrappedCommand = createWrappedKvmCommand(command, this.options.shellHint ?? 'unknown');
|
|
35
|
+
const commandTimeoutMs = this.options.commandTimeoutMs ?? 30000;
|
|
36
|
+
const ocrPollIntervalMs = this.options.ocrPollIntervalMs ?? 500;
|
|
37
|
+
const startedAt = Date.now();
|
|
38
|
+
let rawOcrText = '';
|
|
39
|
+
let attempts = 0;
|
|
40
|
+
await this.options.kvm.focusViewer();
|
|
41
|
+
await this.options.kvm.typeText(wrappedCommand.textToType);
|
|
42
|
+
await this.options.kvm.pressKey('Enter');
|
|
43
|
+
while (true) {
|
|
44
|
+
attempts++;
|
|
45
|
+
rawOcrText = await this.observeText();
|
|
46
|
+
const parsedResult = parseWrappedKvmCommandOutput({
|
|
47
|
+
commandId: wrappedCommand.commandId,
|
|
48
|
+
startMarker: wrappedCommand.startMarker,
|
|
49
|
+
endMarkerPrefix: wrappedCommand.endMarkerPrefix,
|
|
50
|
+
rawText: rawOcrText,
|
|
51
|
+
});
|
|
52
|
+
if (parsedResult.completed) {
|
|
53
|
+
return {
|
|
54
|
+
commandId: wrappedCommand.commandId,
|
|
55
|
+
command,
|
|
56
|
+
completed: true,
|
|
57
|
+
timedOut: false,
|
|
58
|
+
exitCode: parsedResult.exitCode,
|
|
59
|
+
combinedText: parsedResult.combinedText,
|
|
60
|
+
rawOcrText,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const reachedMaxAttempts = typeof this.options.ocrMaxAttempts === 'number' && attempts >= this.options.ocrMaxAttempts;
|
|
64
|
+
const reachedTimeout = Date.now() - startedAt >= commandTimeoutMs;
|
|
65
|
+
if (reachedMaxAttempts || reachedTimeout) {
|
|
66
|
+
return {
|
|
67
|
+
commandId: wrappedCommand.commandId,
|
|
68
|
+
command,
|
|
69
|
+
completed: false,
|
|
70
|
+
timedOut: true,
|
|
71
|
+
combinedText: rawOcrText,
|
|
72
|
+
rawOcrText,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const remainingTimeMs = commandTimeoutMs - (Date.now() - startedAt);
|
|
76
|
+
await this.options.kvm.wait(Math.min(ocrPollIntervalMs, Math.max(remainingTimeMs, 0)));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async observeText() {
|
|
80
|
+
const frame = await this.options.kvm.captureFrame();
|
|
81
|
+
const result = await this.options.ocrEngine.recognize(frame, {
|
|
82
|
+
crop: this.options.ocrCrop,
|
|
83
|
+
language: 'eng',
|
|
84
|
+
});
|
|
85
|
+
return result.text;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5rdm10ZXJtaW5hbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2t2bS9jbGFzc2VzLmt2bXRlcm1pbmFsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSw0QkFBNEIsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBTTdGLE1BQU0sT0FBTyxXQUFXO0lBQ2QsT0FBTyxDQUFzQjtJQUVyQyxZQUFZLE9BQTRCO1FBQ3RDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQ3pCLENBQUM7SUFFTSxLQUFLLENBQUMsU0FBUztRQUNwQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sSUFBSSxTQUFTLENBQUM7UUFFaEQsUUFBUSxNQUFNLEVBQUUsQ0FBQztZQUNmLEtBQUssU0FBUztnQkFDWixNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNwRCxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDakMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsb0JBQW9CLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7Z0JBQ3pDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNsQyxNQUFNO1lBQ1IsS0FBSyxPQUFPO2dCQUNWLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7Z0JBQ3hELE1BQU0sSUFBSSxDQUFD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
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IWrappedKvmCommand, TKvmShellHint } from './interfaces.js';
|
|
2
|
+
export interface IParseWrappedKvmCommandOutputOptions {
|
|
3
|
+
commandId: string;
|
|
4
|
+
startMarker: string;
|
|
5
|
+
endMarkerPrefix: string;
|
|
6
|
+
rawText: string;
|
|
7
|
+
}
|
|
8
|
+
export interface IParseWrappedKvmCommandOutputResult {
|
|
9
|
+
completed: boolean;
|
|
10
|
+
exitCode?: number;
|
|
11
|
+
combinedText: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const createWrappedKvmCommand: (command: string, shellHint: TKvmShellHint) => IWrappedKvmCommand;
|
|
14
|
+
export declare const parseWrappedKvmCommandOutput: (options: IParseWrappedKvmCommandOutputOptions) => IParseWrappedKvmCommandOutputResult;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export const createWrappedKvmCommand = (command, shellHint) => {
|
|
3
|
+
const commandId = plugins.crypto.randomUUID().replace(/-/g, '');
|
|
4
|
+
const startMarker = `SMARTKVM_START_${commandId}`;
|
|
5
|
+
const endMarkerPrefix = `SMARTKVM_END_${commandId}_`;
|
|
6
|
+
let textToType;
|
|
7
|
+
switch (shellHint) {
|
|
8
|
+
case 'powershell':
|
|
9
|
+
textToType = `$__smartkvm_id = "${commandId}"; Write-Output "${startMarker}"; $__smartkvm_status = 0; try { ${command}; if ($null -ne $LASTEXITCODE) { $__smartkvm_status = $LASTEXITCODE } } catch { Write-Output $_; $__smartkvm_status = 1 }; Write-Output "${endMarkerPrefix}$__smartkvm_status"`;
|
|
10
|
+
break;
|
|
11
|
+
case 'cmd':
|
|
12
|
+
textToType = `echo ${startMarker} & ${command} & echo ${endMarkerPrefix}%ERRORLEVEL%`;
|
|
13
|
+
break;
|
|
14
|
+
case 'bash':
|
|
15
|
+
case 'zsh':
|
|
16
|
+
case 'sh':
|
|
17
|
+
case 'unknown':
|
|
18
|
+
default:
|
|
19
|
+
textToType = `printf '\n${startMarker}\n'; ${command}; __smartkvm_status=$?; printf '\n${endMarkerPrefix}%s\n' "$__smartkvm_status"`;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
commandId,
|
|
24
|
+
shellHint,
|
|
25
|
+
command,
|
|
26
|
+
textToType,
|
|
27
|
+
startMarker,
|
|
28
|
+
endMarkerPrefix,
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export const parseWrappedKvmCommandOutput = (options) => {
|
|
32
|
+
const normalizedRawText = options.rawText.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
33
|
+
const startIndex = normalizedRawText.indexOf(options.startMarker);
|
|
34
|
+
if (startIndex === -1) {
|
|
35
|
+
return {
|
|
36
|
+
completed: false,
|
|
37
|
+
combinedText: normalizedRawText,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const contentStartIndex = startIndex + options.startMarker.length;
|
|
41
|
+
const endIndex = normalizedRawText.indexOf(options.endMarkerPrefix, contentStartIndex);
|
|
42
|
+
if (endIndex === -1) {
|
|
43
|
+
return {
|
|
44
|
+
completed: false,
|
|
45
|
+
combinedText: normalizedRawText,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const endMarkerValueStartIndex = endIndex + options.endMarkerPrefix.length;
|
|
49
|
+
const endMarkerLine = normalizedRawText.slice(endMarkerValueStartIndex).split('\n')[0].trim();
|
|
50
|
+
const exitCodeMatch = endMarkerLine.match(/^-?\d+/);
|
|
51
|
+
const exitCode = exitCodeMatch ? Number.parseInt(exitCodeMatch[0], 10) : undefined;
|
|
52
|
+
return {
|
|
53
|
+
completed: true,
|
|
54
|
+
exitCode,
|
|
55
|
+
combinedText: normalizedRawText.slice(contentStartIndex, endIndex).trim(),
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tbWFuZHdyYXBwZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfa3ZtL2NvbW1hbmR3cmFwcGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQWdCeEMsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsQ0FDckMsT0FBZSxFQUNmLFNBQXdCLEVBQ0osRUFBRTtJQUN0QixNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDaEUsTUFBTSxXQUFXLEdBQUcsa0JBQWtCLFNBQVMsRUFBRSxDQUFDO0lBQ2xELE1BQU0sZUFBZSxHQUFHLGdCQUFnQixTQUFTLEdBQUcsQ0FBQztJQUVyRCxJQUFJLFVBQWtCLENBQUM7SUFDdkIsUUFBUSxTQUFTLEVBQUUsQ0FBQztRQUNsQixLQUFLLFlBQVk7WUFDZixVQUFVLEdBQUcscUJBQXFCLFNBQVMsb0JBQW9CLFdBQVcsb0NBQW9DLE9BQU8sNElBQTRJLGVBQWUscUJBQXFCLENBQUM7WUFDdFMsTUFBTTtRQUNSLEtBQUssS0FBSztZQUNSLFVBQVUsR0FBRyxRQUFRLFdBQVcsTUFBTSxPQUFPLFdBQVcsZUFBZSxjQUFjLENBQUM7WUFDdEYsTUFBTTtRQUNSLEtBQUssTUFBTSxDQUFDO1FBQ1osS0FBSyxLQUFLLENBQUM7UUFDWCxLQUFLLElBQUksQ0FBQztRQUNWLEtBQUssU0FBUyxDQUFDO1FBQ2Y7WUFDRSxVQUFVLEdBQUcsYUFBYSxXQUFXLFFBQVEsT0FBTyxxQ0FBcUMsZUFBZSw0QkFBNEIsQ0FBQztZQUNySSxNQUFNO0lBQ1YsQ0FBQztJQUVELE9BQU87UUFDTCxTQUFTO1FBQ1QsU0FBUztRQUNULE9BQU87UUFDUCxVQUFVO1FBQ1YsV0FBVztRQUNYLGVBQWU7S0FDaEIsQ0FBQztBQUNKLENBQUMsQ0FBQztBQUVGLE1BQU0sQ0FBQyxNQUFNLDRCQUE0QixHQUFHLENBQzFDLE9BQTZDLEVBQ1IsRUFBRTtJQUN2QyxNQUFNLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3RGLE1BQU0sVUFBVSxHQUFHLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDbEUsSUFBSSxVQUFVLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUN0QixPQUFPO1lBQ0wsU0FBUyxFQUFFLEtBQUs7WUFDaEIsWUFBWSxFQUFFLGlCQUFpQjtTQUNoQyxDQUFDO0lBQ0osQ0FBQztJQUVELE1BQU0saUJBQWlCLEdBQUcsVUFBVSxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDO0lBQ2xFLE1BQU0sUUFBUSxHQUFHLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsZUFBZSxFQUFFLGlCQUFpQixDQUFDLENBQUM7SUFDdkYsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNwQixPQUFPO1lBQ0wsU0FBUyxFQUFFLEtBQUs7WUFDaEIsWUFBWSxFQUFFLGlCQUFpQjtTQUNoQyxDQUFDO0lBQ0osQ0FBQztJQUVELE1BQU0sd0JBQXdCLEdBQUcsUUFBUSxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDO0lBQzNFLE1BQU0sYUFBYSxHQUFHLGlCQUFpQixDQUFDLEtBQUssQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUM5RixNQUFNLGFBQWEsR0FBRyxhQUFhLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3BELE1BQU0sUUFBUSxHQUFHLGFBQWEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUVuRixPQUFPO1FBQ0wsU0FBUyxFQUFFLElBQUk7UUFDZixRQUFRO1FBQ1IsWUFBWSxFQUFFLGlCQUFpQixDQUFDLEtBQUssQ0FBQyxpQkFBaUIsRUFBRSxRQUFRLENBQUMsQ0FBQyxJQUFJLEVBQUU7S0FDMUUsQ0FBQztBQUNKLENBQUMsQ0FBQyJ9
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './interfaces.js';
|
|
2
|
+
export * from './classes.browserkvm.js';
|
|
3
|
+
export * from './classes.kvmterminal.js';
|
|
4
|
+
export * from './commandwrappers.js';
|
|
5
|
+
export * from './tools.js';
|
|
6
|
+
export * from './ocr.js';
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19rdm0vaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxpQkFBaUIsQ0FBQztBQUNoQyxjQUFjLHlCQUF5QixDQUFDO0FBQ3hDLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLFlBQVksQ0FBQztBQUMzQixjQUFjLFVBQVUsQ0FBQyJ9
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export type TKvmKind = 'jetkvm' | 'glinet' | 'pikvm' | 'tinypilot' | 'generic';
|
|
2
|
+
export type TKvmFrameMimeType = 'image/png' | 'image/jpeg';
|
|
3
|
+
export type TKvmOsHint = 'windows' | 'macos' | 'linux' | 'unknown';
|
|
4
|
+
export type TKvmShellHint = 'powershell' | 'cmd' | 'bash' | 'zsh' | 'sh' | 'unknown';
|
|
5
|
+
export type TKvmKey = 'Enter' | 'Escape' | 'Tab' | 'Backspace' | 'Delete' | 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight' | 'Home' | 'End' | 'PageUp' | 'PageDown' | 'Space' | 'Meta' | 'Control' | 'Alt' | 'Shift' | 'F1' | 'F2' | 'F3' | 'F4' | 'F5' | 'F6' | 'F7' | 'F8' | 'F9' | 'F10' | 'F11' | 'F12' | string;
|
|
6
|
+
export interface IKvmFrame {
|
|
7
|
+
timestamp: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
mimeType: TKvmFrameMimeType;
|
|
11
|
+
dataBase64: string;
|
|
12
|
+
}
|
|
13
|
+
export interface IBrowserKvmOptions {
|
|
14
|
+
url: string;
|
|
15
|
+
kind?: TKvmKind;
|
|
16
|
+
username?: string;
|
|
17
|
+
password?: string;
|
|
18
|
+
headless?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Main element that should receive keyboard focus.
|
|
21
|
+
* Usually video, canvas, or a wrapper around the KVM viewer.
|
|
22
|
+
*/
|
|
23
|
+
viewerSelector?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Element used for frame capture.
|
|
26
|
+
* Defaults to video, then canvas, then viewer screenshot fallback.
|
|
27
|
+
*/
|
|
28
|
+
captureSelector?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Useful for self-signed KVM certificates.
|
|
31
|
+
*/
|
|
32
|
+
ignoreHttpsErrors?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Persist browser session cookies/login state.
|
|
35
|
+
*/
|
|
36
|
+
userDataDir?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Optional browser executable path.
|
|
39
|
+
*/
|
|
40
|
+
executablePath?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Optional timeout for initial load and viewer detection.
|
|
43
|
+
*/
|
|
44
|
+
timeoutMs?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface IKvmTypeTextOptions {
|
|
47
|
+
delayMs?: number;
|
|
48
|
+
}
|
|
49
|
+
export interface IKvmDriver {
|
|
50
|
+
readonly kind: TKvmKind;
|
|
51
|
+
connect: () => Promise<void>;
|
|
52
|
+
disconnect: () => Promise<void>;
|
|
53
|
+
focusViewer: () => Promise<void>;
|
|
54
|
+
captureFrame: () => Promise<IKvmFrame>;
|
|
55
|
+
typeText: (text: string, options?: IKvmTypeTextOptions) => Promise<void>;
|
|
56
|
+
pressKey: (key: TKvmKey) => Promise<void>;
|
|
57
|
+
pressShortcut: (keys: TKvmKey[]) => Promise<void>;
|
|
58
|
+
wait: (milliseconds: number) => Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
export interface IOcrCrop {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
}
|
|
66
|
+
export interface IOcrRecognizeOptions {
|
|
67
|
+
crop?: IOcrCrop;
|
|
68
|
+
language?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface IOcrResult {
|
|
71
|
+
text: string;
|
|
72
|
+
confidence?: number;
|
|
73
|
+
}
|
|
74
|
+
export interface IOcrEngine {
|
|
75
|
+
recognize: (frame: IKvmFrame, options?: IOcrRecognizeOptions) => Promise<IOcrResult>;
|
|
76
|
+
}
|
|
77
|
+
export interface IKvmTerminalOptions {
|
|
78
|
+
kvm: IKvmDriver;
|
|
79
|
+
ocrEngine: IOcrEngine;
|
|
80
|
+
osHint?: TKvmOsHint;
|
|
81
|
+
shellHint?: TKvmShellHint;
|
|
82
|
+
commandTimeoutMs?: number;
|
|
83
|
+
ocrPollIntervalMs?: number;
|
|
84
|
+
ocrMaxAttempts?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Optional OCR crop to limit recognition to terminal area.
|
|
87
|
+
*/
|
|
88
|
+
ocrCrop?: IOcrCrop;
|
|
89
|
+
}
|
|
90
|
+
export interface IKvmTerminalCommandResult {
|
|
91
|
+
commandId: string;
|
|
92
|
+
command: string;
|
|
93
|
+
completed: boolean;
|
|
94
|
+
timedOut: boolean;
|
|
95
|
+
exitCode?: number;
|
|
96
|
+
combinedText: string;
|
|
97
|
+
rawOcrText: string;
|
|
98
|
+
}
|
|
99
|
+
export interface IWrappedKvmCommand {
|
|
100
|
+
commandId: string;
|
|
101
|
+
shellHint: TKvmShellHint;
|
|
102
|
+
command: string;
|
|
103
|
+
textToType: string;
|
|
104
|
+
startMarker: string;
|
|
105
|
+
endMarkerPrefix: string;
|
|
106
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IOcrEngine } from './interfaces.js';
|
|
3
|
+
export interface IKvmMistralOcrEngineOptions {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
model?: string;
|
|
6
|
+
endpointUrl?: string;
|
|
7
|
+
transport?: plugins.ocr.ISmartAiMistralOcrTransport;
|
|
8
|
+
includeImageBase64?: boolean;
|
|
9
|
+
tableFormat?: plugins.ocr.TSmartAiMistralOcrTableFormat;
|
|
10
|
+
extractHeader?: boolean;
|
|
11
|
+
extractFooter?: boolean;
|
|
12
|
+
confidenceScoresGranularity?: plugins.ocr.TSmartAiMistralOcrConfidenceScoresGranularity;
|
|
13
|
+
}
|
|
14
|
+
export declare const createMistralKvmOcrEngine: (options?: IKvmMistralOcrEngineOptions) => IOcrEngine;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export const createMistralKvmOcrEngine = (options = {}) => {
|
|
3
|
+
const ocrEngine = plugins.ocr.createMistralOcrEngine({
|
|
4
|
+
...options,
|
|
5
|
+
confidenceScoresGranularity: options.confidenceScoresGranularity ?? 'page',
|
|
6
|
+
});
|
|
7
|
+
return {
|
|
8
|
+
recognize: async (frame, recognizeOptions = {}) => {
|
|
9
|
+
if (recognizeOptions.crop) {
|
|
10
|
+
throw new Error('Mistral KVM OCR does not support crop options yet.');
|
|
11
|
+
}
|
|
12
|
+
const result = await ocrEngine.recognizeImage({
|
|
13
|
+
dataBase64: frame.dataBase64,
|
|
14
|
+
mimeType: frame.mimeType,
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
text: result.text,
|
|
18
|
+
confidence: result.confidence,
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2NyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfa3ZtL29jci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQWV4QyxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxDQUN2QyxVQUF1QyxFQUFFLEVBQzdCLEVBQUU7SUFDZCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixDQUFDO1FBQ25ELEdBQUcsT0FBTztRQUNWLDJCQUEyQixFQUFFLE9BQU8sQ0FBQywyQkFBMkIsSUFBSSxNQUFNO0tBQzNFLENBQUMsQ0FBQztJQUVILE9BQU87UUFDTCxTQUFTLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxnQkFBZ0IsR0FBRyxFQUFFLEVBQXVCLEVBQUU7WUFDckUsSUFBSSxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDMUIsTUFBTSxJQUFJLEtBQUssQ0FBQyxvREFBb0QsQ0FBQyxDQUFDO1lBQ3hFLENBQUM7WUFFRCxNQUFNLE1BQU0sR0FBRyxNQUFNLFNBQVMsQ0FBQyxjQUFjLENBQUM7Z0JBQzVDLFVBQVUsRUFBRSxLQUFLLENBQUMsVUFBVTtnQkFDNUIsUUFBUSxFQUFFLEtBQUssQ0FBQyxRQUFRO2FBQ3pCLENBQUMsQ0FBQztZQUVILE9BQU87Z0JBQ0wsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJO2dCQUNqQixVQUFVLEVBQUUsTUFBTSxDQUFDLFVBQVU7YUFDOUIsQ0FBQztRQUNKLENBQUM7S0FDRixDQUFDO0FBQ0osQ0FBQyxDQUFDIn0=
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// node native scope
|
|
2
|
+
import * as crypto from 'node:crypto';
|
|
3
|
+
export { crypto };
|
|
4
|
+
// third party scope
|
|
5
|
+
import puppeteer from 'puppeteer';
|
|
6
|
+
export { puppeteer };
|
|
7
|
+
// FlexHarness OCR
|
|
8
|
+
import * as ocr from '@modelprofile.com/flexharness/ocr';
|
|
9
|
+
export { ocr };
|
|
10
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2t2bS9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUV0QyxPQUFPLEVBQUUsTUFBTSxFQUFFLENBQUM7QUFFbEIsb0JBQW9CO0FBQ3BCLE9BQU8sU0FBUyxNQUFNLFdBQVcsQ0FBQztBQUVsQyxPQUFPLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFFckIsa0JBQWtCO0FBQ2xCLE9BQU8sS0FBSyxHQUFHLE1BQU0sbUNBQW1DLENBQUM7QUFDekQsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDIn0=
|