增加小型对象数据库
This commit is contained in:
parent
f925da7d7d
commit
ca64ce040f
@ -2,7 +2,10 @@
|
||||
|
||||
## [main] 0.0.5
|
||||
- 支持对已经打开过的文件项目进行管理
|
||||
- 支持对用户对应服务器的调试工作内容进行保存(MVP 100%)
|
||||
- 支持对用户对应服务器的调试工作内容进行保存
|
||||
- 支持连续工具调用和错误警告的显示
|
||||
- 实现小型本地对象数据库,用于对对话产生的多媒体进行数据持久化
|
||||
- 支持对于调用结果进行一键复现
|
||||
|
||||
## [main] 0.0.4
|
||||
- 修复选择模型后点击确认跳转回 deepseek 的 bug
|
||||
|
@ -51,6 +51,7 @@
|
||||
| `service` | 系统配置信息云同步 | `MVP` | 0% | `P1` |
|
||||
| `all` | 系统提示词管理模块 | `MVP` | 0% | `P1` |
|
||||
| `service` | 工具 wise 的日志系统 | `MVP` | 0% | `P0` |
|
||||
| `service` | 自带 OCR 进行字符识别 | `MVP` | 0% | `P1` |
|
||||
|
||||
|
||||
## Dev
|
||||
|
1257
service/package-lock.json
generated
1257
service/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -41,6 +41,8 @@
|
||||
"pako": "^2.1.0",
|
||||
"pino": "^9.6.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"sqlite": "^5.1.1",
|
||||
"sqlite3": "^5.1.7",
|
||||
"tesseract.js": "^6.0.1",
|
||||
"ws": "^8.18.1"
|
||||
}
|
||||
|
@ -1,140 +1,94 @@
|
||||
|
||||
import { PostMessageble } from '../hook/adapter';
|
||||
import { connect, MCPClient, type MCPOptions } from '../hook/client';
|
||||
import { lookupEnvVarHandler } from './env-var';
|
||||
import { callTool, getPrompt, getServerVersion, listPrompts, listResources, listResourceTemplates, listTools, readResource } from './mcp-server';
|
||||
import { chatCompletionHandler } from './llm';
|
||||
import { panelLoadHandler, panelSaveHandler } from './panel';
|
||||
import { settingLoadHandler, settingSaveHandler } from './setting';
|
||||
import { ping } from './util';
|
||||
import { lookupEnvVarService } from '../service/env-var';
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import {
|
||||
callToolService,
|
||||
getPromptService,
|
||||
getServerVersionService,
|
||||
listPromptsService,
|
||||
listResourcesService,
|
||||
listResourceTemplatesService,
|
||||
listToolsService,
|
||||
readResourceService
|
||||
} from '../service/mcp-server';
|
||||
|
||||
import { abortMessageService, chatCompletionService } from '../service/llm';
|
||||
import { panelLoadService, panelSaveService } from '../service/panel';
|
||||
import { settingLoadService, settingSaveService } from '../service/setting';
|
||||
import { pingService } from '../service/util';
|
||||
import { client, connectService } from '../service/connect';
|
||||
|
||||
// TODO: 支持更多的 client
|
||||
export let client: MCPClient | undefined = undefined;
|
||||
|
||||
function tryGetRunCommandError(command: string, args: string[] = [], cwd?: string): string | null {
|
||||
try {
|
||||
console.log('current command', command);
|
||||
console.log('current args', args);
|
||||
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: cwd || process.cwd(),
|
||||
stdio: 'pipe',
|
||||
encoding: 'utf-8'
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
return result.error.message;
|
||||
}
|
||||
if (result.status !== 0) {
|
||||
return result.stderr || `Command failed with code ${result.status}`;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function connectHandler(option: MCPOptions, webview: PostMessageble) {
|
||||
try {
|
||||
console.log('ready to connect', option);
|
||||
|
||||
client = await connect(option);
|
||||
const connectResult = {
|
||||
code: 200,
|
||||
msg: 'Connect to OpenMCP successfully\nWelcome back, Kirigaya'
|
||||
};
|
||||
webview.postMessage({ command: 'connect', data: connectResult });
|
||||
} catch (error) {
|
||||
|
||||
// TODO: 这边获取到的 error 不够精致,如何才能获取到更加精准的错误
|
||||
// 比如 error: Failed to spawn: `server.py`
|
||||
// Caused by: No such file or directory (os error 2)
|
||||
|
||||
let errorMsg = '';
|
||||
|
||||
if (option.command) {
|
||||
errorMsg += tryGetRunCommandError(option.command, option.args, option.cwd);
|
||||
}
|
||||
|
||||
errorMsg += (error as any).toString();
|
||||
|
||||
const connectResult = {
|
||||
code: 500,
|
||||
msg: errorMsg
|
||||
};
|
||||
webview.postMessage({ command: 'connect', data: connectResult });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function messageController(command: string, data: any, webview: PostMessageble) {
|
||||
switch (command) {
|
||||
case 'connect':
|
||||
connectHandler(data, webview);
|
||||
connectService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'server/version':
|
||||
getServerVersion(client, webview);
|
||||
getServerVersionService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'prompts/list':
|
||||
listPrompts(client, webview);
|
||||
listPromptsService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'prompts/get':
|
||||
getPrompt(client, data, webview);
|
||||
getPromptService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'resources/list':
|
||||
listResources(client, webview);
|
||||
listResourcesService(client, data, webview);
|
||||
break;
|
||||
|
||||
|
||||
case 'resources/templates/list':
|
||||
listResourceTemplates(client, webview);
|
||||
listResourceTemplatesService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'resources/read':
|
||||
readResource(client, data, webview);
|
||||
readResourceService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'tools/list':
|
||||
listTools(client, webview);
|
||||
listToolsService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'tools/call':
|
||||
callTool(client, data, webview);
|
||||
callToolService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'ping':
|
||||
ping(client, webview);
|
||||
pingService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'setting/save':
|
||||
settingSaveHandler(client, data, webview);
|
||||
settingSaveService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'setting/load':
|
||||
settingLoadHandler(client, webview);
|
||||
settingLoadService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'panel/save':
|
||||
panelSaveHandler(client, data, webview);
|
||||
panelSaveService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'panel/load':
|
||||
panelLoadHandler(client, webview);
|
||||
panelLoadService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'llm/chat/completions':
|
||||
chatCompletionHandler(client, data, webview);
|
||||
chatCompletionService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'llm/chat/completions/cancel':
|
||||
abortMessageService(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'lookup-env-var':
|
||||
lookupEnvVarHandler(client, data, webview);
|
||||
lookupEnvVarService(client, data, webview);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
69
service/src/hook/db.ts
Normal file
69
service/src/hook/db.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import sqlite3 from 'sqlite3';
|
||||
import { open } from 'sqlite';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
interface Entity {
|
||||
id: string | number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export class LocalDB<T extends Entity> {
|
||||
private db: any;
|
||||
|
||||
constructor(private tableName: string) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private async init() {
|
||||
// 默认存储在用户目录的 .openmcp 目录下
|
||||
const homedir = os.homedir();
|
||||
const filename = path.join(homedir, '.openmcp', 'db.sqlite');
|
||||
|
||||
this.db = await open({
|
||||
filename,
|
||||
driver: sqlite3.Database
|
||||
});
|
||||
|
||||
await this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
||||
id TEXT PRIMARY KEY,
|
||||
data TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
async insert(entity: T): Promise<void> {
|
||||
await this.db.run(
|
||||
`INSERT OR REPLACE INTO ${this.tableName} (id, data) VALUES (?, ?)`,
|
||||
entity.id,
|
||||
JSON.stringify(entity)
|
||||
);
|
||||
}
|
||||
|
||||
async findById(id: string | number): Promise<T | undefined> {
|
||||
const row = await this.db.get(
|
||||
`SELECT data FROM ${this.tableName} WHERE id = ?`,
|
||||
id
|
||||
);
|
||||
return row ? JSON.parse(row.data) : undefined;
|
||||
}
|
||||
|
||||
async findAll(): Promise<T[]> {
|
||||
const rows = await this.db.all(
|
||||
`SELECT data FROM ${this.tableName}`
|
||||
);
|
||||
return rows.map((row: any) => JSON.parse(row.data));
|
||||
}
|
||||
|
||||
async delete(id: string | number): Promise<void> {
|
||||
await this.db.run(
|
||||
`DELETE FROM ${this.tableName} WHERE id = ?`,
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this.db.close();
|
||||
}
|
||||
}
|
@ -2,4 +2,4 @@ export { messageController } from './controller';
|
||||
export { VSCodeWebViewLike } from './hook/adapter';
|
||||
export { setVscodeWorkspace } from './hook/setting';
|
||||
// TODO: 更加规范
|
||||
export { client } from './controller';
|
||||
export { client } from './service/connect';
|
@ -2,7 +2,7 @@ import WebSocket from 'ws';
|
||||
import pino from 'pino';
|
||||
|
||||
import { messageController } from './controller';
|
||||
import { VSCodeWebViewLike } from './adapter';
|
||||
import { VSCodeWebViewLike } from './hook/adapter';
|
||||
|
||||
export interface VSCodeMessage {
|
||||
command: string;
|
||||
|
66
service/src/service/connect.ts
Normal file
66
service/src/service/connect.ts
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
import { PostMessageble } from '../hook/adapter';
|
||||
import { connect, MCPClient, type MCPOptions } from '../hook/client';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
// TODO: 支持更多的 client
|
||||
export let client: MCPClient | undefined = undefined;
|
||||
|
||||
function tryGetRunCommandError(command: string, args: string[] = [], cwd?: string): string | null {
|
||||
try {
|
||||
console.log('current command', command);
|
||||
console.log('current args', args);
|
||||
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: cwd || process.cwd(),
|
||||
stdio: 'pipe',
|
||||
encoding: 'utf-8'
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
return result.error.message;
|
||||
}
|
||||
if (result.status !== 0) {
|
||||
return result.stderr || `Command failed with code ${result.status}`;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function connectService(
|
||||
client: MCPClient | undefined,
|
||||
option: MCPOptions,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
try {
|
||||
console.log('ready to connect', option);
|
||||
|
||||
client = await connect(option);
|
||||
const connectResult = {
|
||||
code: 200,
|
||||
msg: 'Connect to OpenMCP successfully\nWelcome back, Kirigaya'
|
||||
};
|
||||
webview.postMessage({ command: 'connect', data: connectResult });
|
||||
} catch (error) {
|
||||
|
||||
// TODO: 这边获取到的 error 不够精致,如何才能获取到更加精准的错误
|
||||
// 比如 error: Failed to spawn: `server.py`
|
||||
// Caused by: No such file or directory (os error 2)
|
||||
|
||||
let errorMsg = '';
|
||||
|
||||
if (option.command) {
|
||||
errorMsg += tryGetRunCommandError(option.command, option.args, option.cwd);
|
||||
}
|
||||
|
||||
errorMsg += (error as any).toString();
|
||||
|
||||
const connectResult = {
|
||||
code: 500,
|
||||
msg: errorMsg
|
||||
};
|
||||
webview.postMessage({ command: 'connect', data: connectResult });
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import { PostMessageble } from "../hook/adapter";
|
||||
import { MCPClient } from "../hook/client";
|
||||
|
||||
|
||||
export async function lookupEnvVarHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
export async function lookupEnvVarService(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
try {
|
||||
const { keys } = data;
|
||||
|
@ -4,7 +4,7 @@ import { PostMessageble } from '../hook/adapter';
|
||||
|
||||
let currentStream: AsyncIterable<any> | null = null;
|
||||
|
||||
export async function chatCompletionHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
export async function chatCompletionService(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
@ -100,7 +100,7 @@ export async function chatCompletionHandler(client: MCPClient | undefined, data:
|
||||
}
|
||||
|
||||
// 处理中止消息的函数
|
||||
export function handleAbortMessage(webview: PostMessageble) {
|
||||
export function abortMessageService(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
if (currentStream) {
|
||||
// 标记流已中止
|
||||
currentStream = null;
|
@ -21,8 +21,9 @@ export interface CallToolOption {
|
||||
/**
|
||||
* @description 列出所有 prompts
|
||||
*/
|
||||
export async function listPrompts(
|
||||
export async function listPromptsService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
if (!client) {
|
||||
@ -53,7 +54,7 @@ export async function listPrompts(
|
||||
/**
|
||||
* @description 获取特定 prompt
|
||||
*/
|
||||
export async function getPrompt(
|
||||
export async function getPromptService(
|
||||
client: MCPClient | undefined,
|
||||
option: GetPromptOption,
|
||||
webview: PostMessageble
|
||||
@ -86,8 +87,9 @@ export async function getPrompt(
|
||||
/**
|
||||
* @description 列出所有resources
|
||||
*/
|
||||
export async function listResources(
|
||||
export async function listResourcesService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
if (!client) {
|
||||
@ -119,8 +121,9 @@ export async function listResources(
|
||||
/**
|
||||
* @description 列出所有resources
|
||||
*/
|
||||
export async function listResourceTemplates(
|
||||
export async function listResourceTemplatesService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
if (!client) {
|
||||
@ -152,7 +155,7 @@ export async function listResourceTemplates(
|
||||
/**
|
||||
* @description 读取特定resource
|
||||
*/
|
||||
export async function readResource(
|
||||
export async function readResourceService(
|
||||
client: MCPClient | undefined,
|
||||
option: ReadResourceOption,
|
||||
webview: PostMessageble
|
||||
@ -185,8 +188,9 @@ export async function readResource(
|
||||
/**
|
||||
* @description 获取工具列表
|
||||
*/
|
||||
export async function listTools(
|
||||
export async function listToolsService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
if (!client) {
|
||||
@ -220,7 +224,7 @@ export async function listTools(
|
||||
/**
|
||||
* @description 调用工具
|
||||
*/
|
||||
export async function callTool(
|
||||
export async function callToolService(
|
||||
client: MCPClient | undefined,
|
||||
option: CallToolOption,
|
||||
webview: PostMessageble
|
||||
@ -254,8 +258,9 @@ export async function callTool(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getServerVersion(
|
||||
export async function getServerVersionService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
if (!client) {
|
18
service/src/service/ocr.ts
Normal file
18
service/src/service/ocr.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { PostMessageble } from "../hook/adapter";
|
||||
import { MCPClient } from "../hook/client";
|
||||
|
||||
export function ocrService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
|
||||
|
||||
webview.postMessage({
|
||||
command: 'ping',
|
||||
data: {
|
||||
code: 200,
|
||||
msg: {}
|
||||
}
|
||||
});
|
||||
}
|
@ -2,7 +2,7 @@ import { PostMessageble } from '../hook/adapter';
|
||||
import { loadConfig, loadTabSaveConfig, saveConfig, saveTabSaveConfig } from '../hook/setting';
|
||||
import { MCPClient } from '../hook/client';
|
||||
|
||||
export async function panelSaveHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
export async function panelSaveService(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
try {
|
||||
// 保存配置
|
||||
const serverInfo = client?.getServerVersion();
|
||||
@ -26,7 +26,11 @@ export async function panelSaveHandler(client: MCPClient | undefined, data: any,
|
||||
}
|
||||
}
|
||||
|
||||
export async function panelLoadHandler(client: MCPClient | undefined, webview: PostMessageble) {
|
||||
export async function panelLoadService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
try {
|
||||
// 加载配置
|
||||
const serverInfo = client?.getServerVersion();
|
@ -2,7 +2,11 @@ import { PostMessageble } from '../hook/adapter';
|
||||
import { loadConfig, saveConfig } from '../hook/setting';
|
||||
import { MCPClient } from '../hook/client';
|
||||
|
||||
export async function settingSaveHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||
export async function settingSaveService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
try {
|
||||
// 保存配置
|
||||
saveConfig(data);
|
||||
@ -28,7 +32,11 @@ export async function settingSaveHandler(client: MCPClient | undefined, data: an
|
||||
}
|
||||
}
|
||||
|
||||
export async function settingLoadHandler(client: MCPClient | undefined, webview: PostMessageble) {
|
||||
export async function settingLoadService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
try {
|
||||
// 加载配置
|
||||
const config = loadConfig();
|
@ -1,7 +1,11 @@
|
||||
import { PostMessageble } from "../hook/adapter";
|
||||
import { MCPClient } from "../hook/client";
|
||||
|
||||
export function ping(client: MCPClient | undefined, webview: PostMessageble) {
|
||||
export function pingService(
|
||||
client: MCPClient | undefined,
|
||||
data: any,
|
||||
webview: PostMessageble
|
||||
) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
@ -12,7 +16,8 @@ export function ping(client: MCPClient | undefined, webview: PostMessageble) {
|
||||
}
|
||||
|
||||
webview.postMessage({
|
||||
command: 'ping', data: {
|
||||
command: 'ping',
|
||||
data: {
|
||||
code: 200,
|
||||
msg: {}
|
||||
}
|
@ -10,6 +10,11 @@
|
||||
"declaration": true, // 新增
|
||||
"declarationMap": true // 新增
|
||||
},
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "src/main.ts"] // 排除 main.ts
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user