This commit is contained in:
锦恢 2025-06-20 13:54:04 +08:00
commit 10befe12b4
6 changed files with 1545 additions and 3023 deletions

View File

@ -27,3 +27,4 @@ software/**
.turbo
.github
webpack
.openmcp

4396
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -284,7 +284,7 @@ export class TaskLoop {
/**
* @description Start the loop and asynchronously update the DOM
*/
start(tabStorage: any, userMessage: string): Promise<void>;
start(tabStorage: ChatStorage, userMessage: string): Promise<void>;
/**
* @description Create single conversation context

View File

@ -1,3 +1,5 @@
import * as fs from 'fs';
import { WebSocket } from 'ws';
import { EventEmitter } from 'events';
import { routeMessage } from '../common/router.js';
@ -27,6 +29,10 @@ export interface IConnectionArgs {
cwd?: string;
url?: string;
oauth?: string;
env?: {
[key: string]: string;
};
[key: string]: any;
}
// 监听器回调类型
@ -152,26 +158,78 @@ export class TaskLoopAdapter {
}
}
import { TaskLoop } from '../../task-loop.js';
import * as fs from 'fs';
import chalk from 'chalk';
interface StdioMCPConfig {
command: string;
args: string[];
env?: {
[key: string]: string;
};
description?: string;
prompt?: string;
}
export class OAgent {
private adapter: TaskLoopAdapter;
constructor() {
this.adapter = new TaskLoopAdapter();
interface HttpMCPConfig {
url: string;
type?: string;
env?: {
[key: string]: string;
};
description?: string;
prompt?: string;
}
export interface OmAgentConfiguration {
version: string;
mcpServers: {
[key: string]: StdioMCPConfig | HttpMCPConfig;
};
defaultLLM: {
baseURL: string;
apiToken: string;
model: string;
}
}
public addMcp(mcpOption: IConnectionArgs) {
this.adapter.addMcp(mcpOption);
import { MessageState, type ChatMessage, type ChatSetting, type TaskLoop, type TextMessage } from '../../task-loop.js';
export function UserMessage(content: string): TextMessage {
return {
role: 'user',
content,
extraInfo: {
created: Date.now(),
state: MessageState.None,
serverName: '',
enableXmlWrapper: false
}
}
}
export function AssistantMessage(content: string): TextMessage {
return {
role: 'assistant',
content,
extraInfo: {
created: Date.now(),
state: MessageState.None,
serverName: '',
enableXmlWrapper: false
}
}
}
export class OmAgent {
public _adapter: TaskLoopAdapter;
public _loop?: TaskLoop;
constructor() {
this._adapter = new TaskLoopAdapter();
}
/**
* @description Load MCP configuration from file.
* Supports multiple MCP backends and a default LLM model configuration.
*
* Configuration below can be generated from OpenMCP Client directly: https://kirigaya.cn/openmcp
*
* @example
* Example configuration:
* {
@ -188,8 +246,8 @@ export class OAgent {
* "prompt": "You are a helpful assistant."
* }
* },
* "llm": {
* "baseURL": "https://api.deepseek.com",
* "defaultLLM": {
* "baseURL": "https://api.openmemory.ai",
* "apiToken": "YOUR_API_KEY",
* "model": "deepseek-chat"
* }
@ -198,14 +256,69 @@ export class OAgent {
* @param configPath - Path to the configuration file
*/
public loadMcpConfig(configPath: string) {
if (!fs.existsSync(configPath)) {
console.log(
chalk.red(`× configPath not exists! ${configPath}`),
);
throw new Error(`× configPath not exists! ${configPath}`);
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as OmAgentConfiguration;
const { mcpServers, defaultLLM } = config;
for (const key in mcpServers) {
const mcpConfig = mcpServers[key];
if ('command' in mcpConfig) {
const commandString = (
mcpConfig.command + ' ' + mcpConfig.args.join(' ')
).trim();
this._adapter.addMcp({
commandString,
connectionType: 'STDIO',
env: mcpConfig.env,
description: mcpConfig.description,
prompt: mcpConfig.prompt,
});
} else {
const connectionType: ConnectionType = mcpConfig.type === 'http' ? 'STREAMABLE_HTTP': 'SSE';
this._adapter.addMcp({
url: mcpConfig.url,
env: mcpConfig.env,
connectionType,
description: mcpConfig.description,
prompt: mcpConfig.prompt,
});
}
}
}
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
public async getLoop() {
if (this._loop) {
return this._loop;
}
const adapter = this._adapter;
const { TaskLoop } = await import('../../task-loop.js');
this._loop = new TaskLoop({ adapter, verbose: 1 });
return this._loop;
}
public async start(
messages: ChatMessage[] | string,
settings?: ChatSetting
) {
if (messages.length === 0) {
throw new Error('messages is empty');
}
const loop = await this.getLoop();
const storage = await loop.createStorage(settings);
let userMessage: string;
if (typeof messages === 'string') {
userMessage = messages;
} else {
const lastMessageContent = messages.at(-1)?.content;
if (typeof lastMessageContent === 'string') {
userMessage = lastMessageContent;
} else {
throw new Error('last message content is undefined');
}
}
return await loop.start(storage, userMessage);
}
}

View File

@ -1,4 +1,4 @@
export { routeMessage } from './common/router.js';
export { VSCodeWebViewLike, TaskLoopAdapter } from './hook/adapter.js';
export { VSCodeWebViewLike, TaskLoopAdapter, OmAgent, OmAgentConfiguration } from './hook/adapter.js';
export { setVscodeWorkspace, setRunningCWD } from './hook/setting.js';
export { clientMap } from './mcp/connect.service.js';

View File

@ -284,7 +284,7 @@ export class TaskLoop {
/**
* @description Start the loop and asynchronously update the DOM
*/
start(tabStorage: any, userMessage: string): Promise<void>;
start(tabStorage: ChatStorage, userMessage: string): Promise<void>;
/**
* @description Create single conversation context