feat: Implement LiveCode module with WebSocket and SSE support

- Added config management using `useConfig` for environment variables.
- Created `LiveCode` class to manage WebSocket connections and routing.
- Implemented `SSEManager` for Server-Sent Events handling.
- Developed `WSSManager` for managing WebSocket connections with heartbeat functionality.
- Introduced `ReconnectingWebSocket` class for robust WebSocket client with automatic reconnection.
- Added test files for live application demonstrating WebSocket and TCP server integration.
This commit is contained in:
2026-02-02 23:29:58 +08:00
parent 5774391bbe
commit a76c2235ea
19 changed files with 871 additions and 385 deletions

View File

@@ -1,20 +0,0 @@
import { app } from '@/app.ts';
// import { Hotkeys } from '@kevisual/hot-api';
import { Hotkeys } from './lib.ts';
import { useContextKey } from '@kevisual/context';
app.route({
path: 'key-sender',
// middleware: ['admin-auth']
}).define(async (ctx) => {
let keys = ctx.query.keys;
if (keys.includes(' ')) {
keys = keys.replace(/\s+/g, '+');
}
const hotKeys: Hotkeys = useContextKey('hotkeys', () => new Hotkeys());
if (typeof keys === 'string') {
await hotKeys.pressHotkey({
hotkey: keys,
});
}
ctx.body = 'ok';
}).addTo(app);

View File

@@ -1,89 +0,0 @@
import { keyboard, Key } from "@nut-tree-fork/nut-js";
/**
* 控制功能部分的案件映射
*/
export const keyMap: Record<string, Key> = {
'ctrl': Key.LeftControl,
'leftctrl': Key.LeftControl,
'rightctrl': Key.RightControl,
'alt': Key.LeftAlt,
'leftalt': Key.LeftAlt,
'rightalt': Key.RightAlt,
'shift': Key.LeftShift,
'leftshift': Key.LeftShift,
'rightshift': Key.RightShift,
'meta': Key.LeftSuper,
'cmd': Key.LeftCmd,
'win': Key.LeftWin,
// 根据操作系统选择 Ctrl 或 Command 键
'ctrlorcommand': process.platform === 'darwin' ? Key.LeftCmd : Key.LeftControl,
};
/**
* 将快捷键字符串转换为 Key 枚举值
* @param hotkey
* @returns
*/
export const parseHotkey = (hotkey: string): Key[] => {
return hotkey
.toLowerCase()
.split('+')
.map(key => {
const trimmed = key.trim().toLowerCase();
// 如果是修饰键,从映射表中获取
if (keyMap[trimmed]) {
return keyMap[trimmed];
}
// 如果是字母,转换为大写并查找对应的 Key
if (trimmed.length === 1 && /[a-z]/.test(trimmed)) {
const upperKey = trimmed.toUpperCase();
return Key[upperKey as keyof typeof Key] as Key;
}
// 其他情况直接查找
return Key[trimmed as keyof typeof Key] as Key;
})
.filter((key): key is Key => key !== undefined);
}
type PressHostKeysOptions = {
hotkey: string;
durationMs?: number;
}
export const pressHotkey = async (opts: PressHostKeysOptions): Promise<boolean> => {
const { hotkey, durationMs = 100 } = opts;
const keys = parseHotkey(hotkey);
console.log('准备模拟按下快捷键:', hotkey);
// 同时按下所有键
await keyboard.pressKey(...keys);
// 短暂延迟后释放
await new Promise(resolve => setTimeout(resolve, durationMs));
// 释放所有键
await keyboard.releaseKey(...keys);
return true
}
/**
* 模拟按下一组快捷键,支持逗号分隔的多个快捷键
* @param opts
* @returns
*/
export const pressHotkeys = async (opts: PressHostKeysOptions): Promise<boolean> => {
let { hotkey } = opts;
hotkey = hotkey.replace(/\s+/g, ''); // 去除所有空格
const hotkeyList = hotkey.split(',').map(hk => hk.trim());
if (hotkeyList.length === 0) {
return await pressHotkey({ ...opts, hotkey });
}
for (const hk of hotkeyList) {
await pressHotkey({ ...opts, hotkey: hk });
// 每个快捷键之间稍作延迟
await new Promise(resolve => setTimeout(resolve, 200));
}
return true;
}
export class Hotkeys {
pressHotkey = pressHotkey;
pressHotkeys = pressHotkeys;
}

View File

@@ -5,8 +5,6 @@ import './ai/index.ts';
import './user/index.ts';
import './call/index.ts'
// TODO: 移除
// import './hot-api/key-sender/index.ts';
import './opencode/index.ts';
import './remote/index.ts';
import './kevisual/index.ts'