160 lines
4.2 KiB
TypeScript
160 lines
4.2 KiB
TypeScript
import { WebSocketServer } from 'ws';
|
|
import pino from 'pino';
|
|
|
|
import { routeMessage } from './common/router';
|
|
import { VSCodeWebViewLike } from './hook/adapter';
|
|
import path from 'node:path';
|
|
import * as fs from 'node:fs';
|
|
import { setRunningCWD } from './hook/setting';
|
|
|
|
export interface VSCodeMessage {
|
|
command: string;
|
|
data?: unknown;
|
|
callbackId?: string;
|
|
}
|
|
|
|
const logger = pino({
|
|
transport: {
|
|
target: 'pino-pretty', // 启用 pino-pretty
|
|
options: {
|
|
colorize: true, // 开启颜色
|
|
levelFirst: true, // 先打印日志级别
|
|
translateTime: 'SYS:yyyy-mm-dd HH:MM:ss', // 格式化时间
|
|
ignore: 'pid,hostname', // 忽略部分字段
|
|
}
|
|
}
|
|
});
|
|
|
|
export type MessageHandler = (message: VSCodeMessage) => void;
|
|
|
|
interface IStdioLaunchSignature {
|
|
type: 'STDIO';
|
|
commandString: string;
|
|
cwd: string;
|
|
}
|
|
|
|
interface ISSELaunchSignature {
|
|
type:'SSE';
|
|
url: string;
|
|
oauth: string;
|
|
}
|
|
|
|
export type ILaunchSigature = IStdioLaunchSignature | ISSELaunchSignature;
|
|
|
|
function refreshConnectionOption(envPath: string) {
|
|
const defaultOption = {
|
|
type:'STDIO',
|
|
command: 'mcp',
|
|
args: ['run', 'main.py'],
|
|
cwd: '../server'
|
|
};
|
|
|
|
fs.writeFileSync(envPath, JSON.stringify(defaultOption, null, 4));
|
|
|
|
return defaultOption;
|
|
}
|
|
|
|
function getInitConnectionOption() {
|
|
const envPath = path.join(__dirname, '..', '.env');
|
|
|
|
if (!fs.existsSync(envPath)) {
|
|
return refreshConnectionOption(envPath);
|
|
}
|
|
|
|
try {
|
|
const option = JSON.parse(fs.readFileSync(envPath, 'utf-8'));
|
|
return option;
|
|
|
|
} catch (error) {
|
|
logger.error('读取 .env 配置文件');
|
|
return refreshConnectionOption(envPath);
|
|
}
|
|
}
|
|
|
|
function updateConnectionOption(data: any) {
|
|
const envPath = path.join(__dirname, '..', '.env');
|
|
|
|
if (data.connectionType === 'STDIO') {
|
|
const connectionItem = {
|
|
type: 'STDIO',
|
|
command: data.command,
|
|
args: data.args,
|
|
cwd: data.cwd.replace(/\\/g, '/')
|
|
};
|
|
|
|
fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4));
|
|
} else {
|
|
const connectionItem = {
|
|
type: 'SSE',
|
|
url: data.url,
|
|
oauth: data.oauth
|
|
};
|
|
|
|
fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4));
|
|
}
|
|
}
|
|
|
|
const devHome = path.join(__dirname, '..', '..');
|
|
setRunningCWD(devHome);
|
|
|
|
const wss = new WebSocketServer({ port: 8282 });
|
|
|
|
console.log('listen on ws://localhost:8282');
|
|
|
|
wss.on('connection', (ws: any) => {
|
|
|
|
// 仿造 webview 进行统一接口访问
|
|
const webview = new VSCodeWebViewLike(ws);
|
|
|
|
// 先发送成功建立的消息
|
|
webview.postMessage({
|
|
command: 'hello',
|
|
data: {
|
|
version: '0.0.1',
|
|
name: '消息桥连接完成'
|
|
}
|
|
});
|
|
|
|
const option = getInitConnectionOption();
|
|
|
|
// 注册消息接受的管线
|
|
webview.onDidReceiveMessage(message => {
|
|
logger.info(`command: [${message.command || 'No Command'}]`);
|
|
const { command, data } = message;
|
|
|
|
switch (command) {
|
|
case 'web/launch-signature':
|
|
const launchResultMessage: ILaunchSigature = option.type === 'STDIO' ?
|
|
{
|
|
type: 'STDIO',
|
|
commandString: option.command + ' ' + option.args.join(' '),
|
|
cwd: option.cwd || ''
|
|
} :
|
|
{
|
|
type: 'SSE',
|
|
url: option.url,
|
|
oauth: option.oauth || ''
|
|
};
|
|
|
|
const launchResult = {
|
|
code: 200,
|
|
msg: launchResultMessage
|
|
};
|
|
|
|
webview.postMessage({
|
|
command: 'web/launch-signature',
|
|
data: launchResult
|
|
});
|
|
|
|
break;
|
|
|
|
case 'web/update-connection-signature':
|
|
updateConnectionOption(data);
|
|
break;
|
|
|
|
default:
|
|
routeMessage(command, data, webview);
|
|
break;
|
|
}
|
|
});
|
|
}); |