@horuzhenko/input-sequencer
v6.0.12
Published
Планировщик ввода для последовательного выполнения команд ввода с задержками
Readme
@horuzhenko/input-sequencer
Планировщик ввода для последовательного выполнения команд ввода с задержками. Предотвращает гонку состояний при отправке команд от LLM и делает взаимодействие с удаленным рабочим столом более стабильным и похожим на человеческое.
Установка
pnpm add @horuzhenko/input-sequencerОсновные возможности
- Последовательное выполнение: Гарантирует выполнение операций ввода в правильном порядке
- Автоматические задержки: Добавляет реалистичные задержки между операциями
- Сложная логика click: Разбивает клик на move → delay → mousedown → delay → mouseup
- Очереди по сессиям: Отдельная очередь для каждой сессии
- Обработка ошибок: Продолжает работу даже при ошибках в отдельных операциях
Использование
Базовый пример
import { InputSequencer } from '@horuzhenko/input-sequencer';
import { GuacAdapter } from '@horuzhenko/guac-adapter';
// Создание адаптера и планировщика
const guacAdapter = new GuacAdapter();
const sequencer = new InputSequencer(guacAdapter);
// Подключение к сессии
const sessionInfo = await guacAdapter.connect({
auth: { baseUrl: 'https://guacamole.example.com', token: 'your-token' }
});
// Добавление операций в очередь
await sequencer.enqueue(sessionInfo.id, {
kind: 'move',
data: { x: 100, y: 100 }
});
await sequencer.enqueue(sessionInfo.id, {
kind: 'click',
data: { x: 100, y: 100, button: 'left' }
});
await sequencer.enqueue(sessionInfo.id, {
kind: 'typeText',
data: { text: 'Hello, World!' }
});
// Ожидание завершения всех операций
await sequencer.flush(sessionInfo.id);Сложные операции
// Двойной клик
await sequencer.enqueue(sessionInfo.id, {
kind: 'click',
data: { x: 200, y: 150, button: 'left', count: 2 }
});
// Комбинация клавиш
await sequencer.enqueue(sessionInfo.id, {
kind: 'keyCombo',
data: { keys: ['Ctrl', 'c'] }
});
// Прокрутка
await sequencer.enqueue(sessionInfo.id, {
kind: 'scroll',
data: { x: 300, y: 300, deltaY: -100 }
});Управление очередью
// Очистка очереди
sequencer.clear(sessionInfo.id);
// Принудительное выполнение всех операций
await sequencer.flush(sessionInfo.id);API
InputSequencer
enqueue(sessionId: SessionId, operation: InputOperation): Promise<void>
Добавляет операцию в очередь для указанной сессии. Если очередь пуста, немедленно начинает обработку.
Параметры:
sessionId- Идентификатор сессииoperation- Операция ввода
Поддерживаемые операции:
{ kind: "move", data: MouseMove }{ kind: "click", data: MouseClick }{ kind: "scroll", data: Scroll }{ kind: "keyCombo", data: KeyCombo }{ kind: "typeText", data: TypeText }
flush(sessionId: SessionId): Promise<void>
Ожидает завершения всех операций в очереди для указанной сессии.
clear(sessionId: SessionId): void
Очищает очередь операций для указанной сессии.
Внутренняя логика
Задержки
- Между операциями: 20ms
- Click операция: move → 20ms → mousedown → 50ms → mouseup
- Двойной клик: дополнительная задержка 100ms между кликами
Обработка click
Операция click автоматически разбивается на последовательность:
moveк координатам клика- Задержка 20ms
mousedownс указанной кнопкой- Задержка 50ms
mouseup
Для двойного клика (count: 2) последовательность повторяется с дополнительной задержкой.
Тестирование
# Запуск тестов
pnpm test
# Запуск тестов в режиме наблюдения
pnpm test --watchРазработка
# Сборка
pnpm build
# Разработка с автоматической пересборкой
pnpm dev
# Проверка типов
pnpm lintПримеры использования
Автоматизация GUI
// Открытие приложения и ввод текста
await sequencer.enqueue(sessionId, { kind: 'keyCombo', data: { keys: ['Ctrl', 'Alt', 't'] } });
await sequencer.enqueue(sessionId, { kind: 'typeText', data: { text: 'notepad' } });
await sequencer.enqueue(sessionId, { kind: 'keyCombo', data: { keys: ['Enter'] } });
await sequencer.enqueue(sessionId, { kind: 'typeText', data: { text: 'Hello from InputSequencer!' } });
await sequencer.flush(sessionId);Работа с формами
// Заполнение формы
await sequencer.enqueue(sessionId, { kind: 'click', data: { x: 100, y: 50, button: 'left' } });
await sequencer.enqueue(sessionId, { kind: 'typeText', data: { text: 'username' } });
await sequencer.enqueue(sessionId, { kind: 'keyCombo', data: { keys: ['Tab'] } });
await sequencer.enqueue(sessionId, { kind: 'typeText', data: { text: 'password' } });
await sequencer.enqueue(sessionId, { kind: 'keyCombo', data: { keys: ['Enter'] } });
await sequencer.flush(sessionId);