Initialize smartkvm package

This commit is contained in:
2026-05-16 13:41:55 +00:00
commit 8588c6c70d
18 changed files with 8751 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
import { createWrappedKvmCommand, parseWrappedKvmCommandOutput } from './smartkvm.commandwrappers.js';
import type {
IKvmTerminalCommandResult,
IKvmTerminalOptions,
} from './smartkvm.interfaces.js';
export class SmartKvmTerminal {
private options: IKvmTerminalOptions;
constructor(options: IKvmTerminalOptions) {
this.options = options;
}
public async bootstrap(): Promise<void> {
const osHint = this.options.osHint ?? 'unknown';
switch (osHint) {
case 'windows':
await this.options.kvm.pressShortcut(['Meta', 'R']);
await this.options.kvm.wait(500);
await this.options.kvm.typeText('powershell -NoLogo');
await this.options.kvm.pressKey('Enter');
await this.options.kvm.wait(1500);
break;
case 'macos':
await this.options.kvm.pressShortcut(['Meta', 'Space']);
await this.options.kvm.wait(500);
await this.options.kvm.typeText('Terminal');
await this.options.kvm.pressKey('Enter');
await this.options.kvm.wait(1500);
break;
case 'linux':
await this.options.kvm.pressShortcut(['Control', 'Alt', 'T']);
await this.options.kvm.wait(1500);
break;
case 'unknown':
default:
break;
}
}
public async runCommand(command: string): Promise<IKvmTerminalCommandResult> {
const wrappedCommand = createWrappedKvmCommand(command, this.options.shellHint ?? 'unknown');
const commandTimeoutMs = this.options.commandTimeoutMs ?? 30000;
const ocrPollIntervalMs = this.options.ocrPollIntervalMs ?? 500;
const startedAt = Date.now();
let rawOcrText = '';
let attempts = 0;
await this.options.kvm.focusViewer();
await this.options.kvm.typeText(wrappedCommand.textToType);
await this.options.kvm.pressKey('Enter');
while (true) {
attempts++;
rawOcrText = await this.observeText();
const parsedResult = parseWrappedKvmCommandOutput({
commandId: wrappedCommand.commandId,
startMarker: wrappedCommand.startMarker,
endMarkerPrefix: wrappedCommand.endMarkerPrefix,
rawText: rawOcrText,
});
if (parsedResult.completed) {
return {
commandId: wrappedCommand.commandId,
command,
completed: true,
timedOut: false,
exitCode: parsedResult.exitCode,
combinedText: parsedResult.combinedText,
rawOcrText,
};
}
const reachedMaxAttempts =
typeof this.options.ocrMaxAttempts === 'number' && attempts >= this.options.ocrMaxAttempts;
const reachedTimeout = Date.now() - startedAt >= commandTimeoutMs;
if (reachedMaxAttempts || reachedTimeout) {
return {
commandId: wrappedCommand.commandId,
command,
completed: false,
timedOut: true,
combinedText: rawOcrText,
rawOcrText,
};
}
const remainingTimeMs = commandTimeoutMs - (Date.now() - startedAt);
await this.options.kvm.wait(Math.min(ocrPollIntervalMs, Math.max(remainingTimeMs, 0)));
}
}
public async observeText(): Promise<string> {
const frame = await this.options.kvm.captureFrame();
const result = await this.options.ocrEngine.recognize(frame, {
crop: this.options.ocrCrop,
language: 'eng',
});
return result.text;
}
}