genesis-ai-cli 7.4.7 → 7.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/dispatcher.js +74 -4
- package/dist/src/mcp/cache.d.ts +100 -0
- package/dist/src/mcp/cache.js +395 -0
- package/dist/src/mcp/index.d.ts +15 -1
- package/dist/src/mcp/index.js +37 -7
- package/dist/src/mcp/multimodal.d.ts +52 -0
- package/dist/src/mcp/multimodal.js +355 -0
- package/dist/src/mcp/parallel-executor.d.ts +113 -0
- package/dist/src/mcp/parallel-executor.js +335 -0
- package/dist/src/mcp/streaming.d.ts +78 -0
- package/dist/src/mcp/streaming.js +345 -0
- package/dist/src/mcp/tool-chain.d.ts +79 -0
- package/dist/src/mcp/tool-chain.js +323 -0
- package/dist/src/mcp/transformers.d.ts +156 -0
- package/dist/src/mcp/transformers.js +362 -0
- package/package.json +1 -1
package/dist/src/mcp/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Genesis
|
|
3
|
+
* Genesis 7.5 - Real MCP Client Module
|
|
4
4
|
*
|
|
5
5
|
* Connects to actual MCP servers using @modelcontextprotocol/sdk.
|
|
6
6
|
* Spawns servers on demand and manages connections.
|
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
* - GENESIS_MCP_MODE: 'real' | 'simulated' | 'hybrid' (default: 'simulated')
|
|
10
10
|
* - GENESIS_MCP_TIMEOUT: Timeout in ms (default: 30000)
|
|
11
11
|
* - GENESIS_MCP_LOG: Enable MCP call logging (default: false)
|
|
12
|
+
*
|
|
13
|
+
* New in 7.5: Frontier MCP capabilities
|
|
14
|
+
* - Tool Chaining: Automatic orchestration of dependent tool calls
|
|
15
|
+
* - Streaming: Real-time result streaming with progress
|
|
16
|
+
* - Multimodal: Image/media display in terminal
|
|
17
|
+
* - Cache: Intelligent per-server caching with TTL
|
|
18
|
+
* - DAG Executor: Parallel execution with dependency awareness
|
|
19
|
+
* - Transformers: Composable result transformations
|
|
12
20
|
*/
|
|
13
21
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
22
|
if (k2 === undefined) k2 = k;
|
|
@@ -33,6 +41,13 @@ exports.isSimulatedResult = isSimulatedResult;
|
|
|
33
41
|
exports.logMCPMode = logMCPMode;
|
|
34
42
|
// Re-export Phase 8: Resilient MCP Wrapper
|
|
35
43
|
__exportStar(require("./resilient.js"), exports);
|
|
44
|
+
// Re-export Phase 10: Frontier MCP Capabilities
|
|
45
|
+
__exportStar(require("./tool-chain.js"), exports);
|
|
46
|
+
__exportStar(require("./streaming.js"), exports);
|
|
47
|
+
__exportStar(require("./multimodal.js"), exports);
|
|
48
|
+
__exportStar(require("./cache.js"), exports);
|
|
49
|
+
__exportStar(require("./parallel-executor.js"), exports);
|
|
50
|
+
__exportStar(require("./transformers.js"), exports);
|
|
36
51
|
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
37
52
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
38
53
|
const crypto_1 = require("crypto");
|
|
@@ -77,7 +92,7 @@ const MCP_SERVER_REGISTRY = {
|
|
|
77
92
|
'brave-search': {
|
|
78
93
|
command: 'npx',
|
|
79
94
|
args: () => ['-y', '@brave/brave-search-mcp-server', '--brave-api-key', process.env.BRAVE_API_KEY || ''],
|
|
80
|
-
tools: ['brave_web_search', 'brave_local_search', 'brave_news_search', 'brave_image_search', 'brave_video_search'],
|
|
95
|
+
tools: ['brave_web_search', 'brave_local_search', 'brave_news_search', 'brave_image_search', 'brave_video_search', 'brave_summarizer'],
|
|
81
96
|
},
|
|
82
97
|
'exa': {
|
|
83
98
|
command: 'npx',
|
|
@@ -89,7 +104,7 @@ const MCP_SERVER_REGISTRY = {
|
|
|
89
104
|
command: 'npx',
|
|
90
105
|
args: ['-y', 'firecrawl-mcp'],
|
|
91
106
|
envVars: () => ({ FIRECRAWL_API_KEY: process.env.FIRECRAWL_API_KEY || '' }),
|
|
92
|
-
tools: ['firecrawl_scrape', 'firecrawl_search', 'firecrawl_map', 'firecrawl_crawl', 'firecrawl_extract'],
|
|
107
|
+
tools: ['firecrawl_scrape', 'firecrawl_search', 'firecrawl_map', 'firecrawl_crawl', 'firecrawl_check_crawl_status', 'firecrawl_extract', 'firecrawl_agent', 'firecrawl_agent_status'],
|
|
93
108
|
},
|
|
94
109
|
// CREATION
|
|
95
110
|
'openai': {
|
|
@@ -105,25 +120,40 @@ const MCP_SERVER_REGISTRY = {
|
|
|
105
120
|
envVars: () => ({
|
|
106
121
|
GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_PERSONAL_ACCESS_TOKEN || process.env.GITHUB_TOKEN || ''
|
|
107
122
|
}),
|
|
108
|
-
tools: [
|
|
123
|
+
tools: [
|
|
124
|
+
'create_repository', 'search_repositories', 'create_issue', 'list_issues', 'get_issue', 'update_issue',
|
|
125
|
+
'add_issue_comment', 'create_pull_request', 'get_pull_request', 'list_pull_requests', 'merge_pull_request',
|
|
126
|
+
'get_pull_request_files', 'create_pull_request_review', 'get_file_contents', 'create_or_update_file',
|
|
127
|
+
'push_files', 'create_branch', 'list_commits', 'fork_repository', 'search_code', 'search_issues', 'search_users'
|
|
128
|
+
],
|
|
109
129
|
},
|
|
110
130
|
// VISUAL
|
|
111
131
|
'stability-ai': {
|
|
112
132
|
command: 'npx',
|
|
113
133
|
args: ['-y', 'mcp-server-stability-ai'],
|
|
114
134
|
envVars: () => ({ STABILITY_AI_API_KEY: process.env.STABILITY_AI_API_KEY || '' }),
|
|
115
|
-
tools: [
|
|
135
|
+
tools: [
|
|
136
|
+
'stability-ai-generate-image', 'stability-ai-generate-image-sd35', 'stability-ai-remove-background',
|
|
137
|
+
'stability-ai-outpaint', 'stability-ai-search-and-replace', 'stability-ai-upscale-fast',
|
|
138
|
+
'stability-ai-upscale-creative', 'stability-ai-control-sketch', 'stability-ai-0-list-resources',
|
|
139
|
+
'stability-ai-search-and-recolor', 'stability-ai-replace-background-and-relight',
|
|
140
|
+
'stability-ai-control-style', 'stability-ai-control-structure'
|
|
141
|
+
],
|
|
116
142
|
},
|
|
117
143
|
// STORAGE
|
|
118
144
|
'memory': {
|
|
119
145
|
command: 'npx',
|
|
120
146
|
args: ['-y', '@modelcontextprotocol/server-memory'],
|
|
121
|
-
tools: ['create_entities', 'create_relations', 'search_nodes', '
|
|
147
|
+
tools: ['create_entities', 'create_relations', 'add_observations', 'delete_entities', 'delete_relations', 'delete_observations', 'search_nodes', 'open_nodes', 'read_graph'],
|
|
122
148
|
},
|
|
123
149
|
'filesystem': {
|
|
124
150
|
command: 'npx',
|
|
125
151
|
args: () => ['-y', '@modelcontextprotocol/server-filesystem', process.env.HOME || '/tmp'],
|
|
126
|
-
tools: [
|
|
152
|
+
tools: [
|
|
153
|
+
'read_file', 'read_text_file', 'read_media_file', 'read_multiple_files', 'write_file', 'edit_file',
|
|
154
|
+
'create_directory', 'list_directory', 'list_directory_with_sizes', 'directory_tree',
|
|
155
|
+
'move_file', 'search_files', 'get_file_info', 'list_allowed_directories'
|
|
156
|
+
],
|
|
127
157
|
},
|
|
128
158
|
};
|
|
129
159
|
exports.MCP_SERVER_REGISTRY = MCP_SERVER_REGISTRY;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Genesis MCP Multimodal Handler
|
|
3
|
+
*
|
|
4
|
+
* Handles image, audio, and other media types from MCP tool results.
|
|
5
|
+
* Provides terminal display, file management, and format conversion.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Detect media in tool results
|
|
9
|
+
* - Display images in terminal (iTerm2, Kitty, sixel)
|
|
10
|
+
* - Open media with system default apps
|
|
11
|
+
* - Convert between formats
|
|
12
|
+
* - Thumbnail generation
|
|
13
|
+
*/
|
|
14
|
+
export type MediaType = 'image' | 'audio' | 'video' | 'document' | 'unknown';
|
|
15
|
+
export interface MediaInfo {
|
|
16
|
+
type: MediaType;
|
|
17
|
+
mimeType: string;
|
|
18
|
+
path?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
base64?: string;
|
|
21
|
+
width?: number;
|
|
22
|
+
height?: number;
|
|
23
|
+
size?: number;
|
|
24
|
+
format?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface DisplayOptions {
|
|
27
|
+
maxWidth?: number;
|
|
28
|
+
maxHeight?: number;
|
|
29
|
+
autoOpen?: boolean;
|
|
30
|
+
inline?: boolean;
|
|
31
|
+
protocol?: TerminalProtocol;
|
|
32
|
+
}
|
|
33
|
+
export type TerminalProtocol = 'iterm2' | 'kitty' | 'sixel' | 'ascii' | 'none';
|
|
34
|
+
export interface MultimodalResult {
|
|
35
|
+
detected: boolean;
|
|
36
|
+
media: MediaInfo[];
|
|
37
|
+
displayedInline: boolean;
|
|
38
|
+
openedExternally: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function detectMediaType(input: string | MediaInfo): MediaType;
|
|
41
|
+
export declare function extractMediaFromResult(result: any): MediaInfo[];
|
|
42
|
+
export declare function detectTerminalProtocol(): TerminalProtocol;
|
|
43
|
+
export declare function displayImageInline(imagePath: string, options?: DisplayOptions): Promise<boolean>;
|
|
44
|
+
export declare function openWithSystemViewer(filePath: string): Promise<boolean>;
|
|
45
|
+
export declare class MultimodalHandler {
|
|
46
|
+
private options;
|
|
47
|
+
constructor(options?: DisplayOptions);
|
|
48
|
+
handleResult(result: any): Promise<MultimodalResult>;
|
|
49
|
+
setOptions(options: Partial<DisplayOptions>): void;
|
|
50
|
+
}
|
|
51
|
+
export declare function getMultimodalHandler(options?: DisplayOptions): MultimodalHandler;
|
|
52
|
+
export declare function handleMultimodalResult(result: any): Promise<MultimodalResult>;
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Genesis MCP Multimodal Handler
|
|
4
|
+
*
|
|
5
|
+
* Handles image, audio, and other media types from MCP tool results.
|
|
6
|
+
* Provides terminal display, file management, and format conversion.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Detect media in tool results
|
|
10
|
+
* - Display images in terminal (iTerm2, Kitty, sixel)
|
|
11
|
+
* - Open media with system default apps
|
|
12
|
+
* - Convert between formats
|
|
13
|
+
* - Thumbnail generation
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.MultimodalHandler = void 0;
|
|
50
|
+
exports.detectMediaType = detectMediaType;
|
|
51
|
+
exports.extractMediaFromResult = extractMediaFromResult;
|
|
52
|
+
exports.detectTerminalProtocol = detectTerminalProtocol;
|
|
53
|
+
exports.displayImageInline = displayImageInline;
|
|
54
|
+
exports.openWithSystemViewer = openWithSystemViewer;
|
|
55
|
+
exports.getMultimodalHandler = getMultimodalHandler;
|
|
56
|
+
exports.handleMultimodalResult = handleMultimodalResult;
|
|
57
|
+
const child_process_1 = require("child_process");
|
|
58
|
+
const util_1 = require("util");
|
|
59
|
+
const fs = __importStar(require("fs"));
|
|
60
|
+
const path = __importStar(require("path"));
|
|
61
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Media Detection
|
|
64
|
+
// ============================================================================
|
|
65
|
+
const MIME_TYPE_MAP = {
|
|
66
|
+
'image/png': 'image',
|
|
67
|
+
'image/jpeg': 'image',
|
|
68
|
+
'image/gif': 'image',
|
|
69
|
+
'image/webp': 'image',
|
|
70
|
+
'image/svg+xml': 'image',
|
|
71
|
+
'audio/mpeg': 'audio',
|
|
72
|
+
'audio/wav': 'audio',
|
|
73
|
+
'audio/ogg': 'audio',
|
|
74
|
+
'video/mp4': 'video',
|
|
75
|
+
'video/webm': 'video',
|
|
76
|
+
'application/pdf': 'document',
|
|
77
|
+
};
|
|
78
|
+
const EXTENSION_MAP = {
|
|
79
|
+
'.png': 'image',
|
|
80
|
+
'.jpg': 'image',
|
|
81
|
+
'.jpeg': 'image',
|
|
82
|
+
'.gif': 'image',
|
|
83
|
+
'.webp': 'image',
|
|
84
|
+
'.svg': 'image',
|
|
85
|
+
'.mp3': 'audio',
|
|
86
|
+
'.wav': 'audio',
|
|
87
|
+
'.ogg': 'audio',
|
|
88
|
+
'.mp4': 'video',
|
|
89
|
+
'.webm': 'video',
|
|
90
|
+
'.pdf': 'document',
|
|
91
|
+
};
|
|
92
|
+
function detectMediaType(input) {
|
|
93
|
+
if (typeof input !== 'string') {
|
|
94
|
+
return input.type;
|
|
95
|
+
}
|
|
96
|
+
// Check extension
|
|
97
|
+
const ext = path.extname(input).toLowerCase();
|
|
98
|
+
if (EXTENSION_MAP[ext]) {
|
|
99
|
+
return EXTENSION_MAP[ext];
|
|
100
|
+
}
|
|
101
|
+
// Check if it's a data URL
|
|
102
|
+
if (input.startsWith('data:')) {
|
|
103
|
+
const mimeMatch = input.match(/^data:([^;,]+)/);
|
|
104
|
+
if (mimeMatch && MIME_TYPE_MAP[mimeMatch[1]]) {
|
|
105
|
+
return MIME_TYPE_MAP[mimeMatch[1]];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return 'unknown';
|
|
109
|
+
}
|
|
110
|
+
function extractMediaFromResult(result) {
|
|
111
|
+
const media = [];
|
|
112
|
+
if (!result)
|
|
113
|
+
return media;
|
|
114
|
+
// Check for common image result patterns
|
|
115
|
+
const imagePatterns = [
|
|
116
|
+
'imagePath', 'outputPath', 'path', 'filePath', 'file_path',
|
|
117
|
+
'imageUrl', 'url', 'src', 'image', 'output',
|
|
118
|
+
];
|
|
119
|
+
for (const pattern of imagePatterns) {
|
|
120
|
+
if (result[pattern] && typeof result[pattern] === 'string') {
|
|
121
|
+
const value = result[pattern];
|
|
122
|
+
const type = detectMediaType(value);
|
|
123
|
+
if (type !== 'unknown') {
|
|
124
|
+
media.push({
|
|
125
|
+
type,
|
|
126
|
+
mimeType: getMimeType(value),
|
|
127
|
+
path: value.startsWith('/') || value.startsWith('.') ? value : undefined,
|
|
128
|
+
url: value.startsWith('http') ? value : undefined,
|
|
129
|
+
base64: value.startsWith('data:') ? value.split(',')[1] : undefined,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Check for base64 data
|
|
135
|
+
if (result.base64 || result.data) {
|
|
136
|
+
const base64Data = result.base64 || result.data;
|
|
137
|
+
if (typeof base64Data === 'string' && base64Data.length > 100) {
|
|
138
|
+
media.push({
|
|
139
|
+
type: 'image',
|
|
140
|
+
mimeType: result.mimeType || 'image/png',
|
|
141
|
+
base64: base64Data,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Check for arrays of results
|
|
146
|
+
if (Array.isArray(result.images || result.files || result.outputs)) {
|
|
147
|
+
const items = result.images || result.files || result.outputs;
|
|
148
|
+
for (const item of items) {
|
|
149
|
+
media.push(...extractMediaFromResult(item));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return media;
|
|
153
|
+
}
|
|
154
|
+
function getMimeType(pathOrUrl) {
|
|
155
|
+
const ext = path.extname(pathOrUrl).toLowerCase();
|
|
156
|
+
const mimeMap = {
|
|
157
|
+
'.png': 'image/png',
|
|
158
|
+
'.jpg': 'image/jpeg',
|
|
159
|
+
'.jpeg': 'image/jpeg',
|
|
160
|
+
'.gif': 'image/gif',
|
|
161
|
+
'.webp': 'image/webp',
|
|
162
|
+
'.svg': 'image/svg+xml',
|
|
163
|
+
'.mp3': 'audio/mpeg',
|
|
164
|
+
'.wav': 'audio/wav',
|
|
165
|
+
'.mp4': 'video/mp4',
|
|
166
|
+
'.pdf': 'application/pdf',
|
|
167
|
+
};
|
|
168
|
+
return mimeMap[ext] || 'application/octet-stream';
|
|
169
|
+
}
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// Terminal Display
|
|
172
|
+
// ============================================================================
|
|
173
|
+
function detectTerminalProtocol() {
|
|
174
|
+
const term = process.env.TERM_PROGRAM || '';
|
|
175
|
+
const termEnv = process.env.TERM || '';
|
|
176
|
+
if (term === 'iTerm.app') {
|
|
177
|
+
return 'iterm2';
|
|
178
|
+
}
|
|
179
|
+
if (term === 'kitty' || termEnv.includes('kitty')) {
|
|
180
|
+
return 'kitty';
|
|
181
|
+
}
|
|
182
|
+
if (termEnv.includes('xterm') && process.env.SIXEL_SUPPORT === 'true') {
|
|
183
|
+
return 'sixel';
|
|
184
|
+
}
|
|
185
|
+
// Fallback to ASCII art representation
|
|
186
|
+
return 'ascii';
|
|
187
|
+
}
|
|
188
|
+
async function displayImageInline(imagePath, options = {}) {
|
|
189
|
+
const protocol = options.protocol || detectTerminalProtocol();
|
|
190
|
+
try {
|
|
191
|
+
switch (protocol) {
|
|
192
|
+
case 'iterm2':
|
|
193
|
+
return await displayITerm2(imagePath, options);
|
|
194
|
+
case 'kitty':
|
|
195
|
+
return await displayKitty(imagePath, options);
|
|
196
|
+
case 'sixel':
|
|
197
|
+
return await displaySixel(imagePath, options);
|
|
198
|
+
case 'ascii':
|
|
199
|
+
return await displayAscii(imagePath, options);
|
|
200
|
+
default:
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
console.error(`[Multimodal] Display error: ${error}`);
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function displayITerm2(imagePath, options) {
|
|
210
|
+
try {
|
|
211
|
+
const data = await fs.promises.readFile(imagePath);
|
|
212
|
+
const base64 = data.toString('base64');
|
|
213
|
+
const width = options.maxWidth || 80;
|
|
214
|
+
const height = options.maxHeight || 24;
|
|
215
|
+
// iTerm2 inline image protocol
|
|
216
|
+
const osc = '\x1b]1337;File=';
|
|
217
|
+
const params = `inline=1;width=${width};height=${height};preserveAspectRatio=1`;
|
|
218
|
+
const st = '\x07';
|
|
219
|
+
process.stdout.write(`${osc}${params}:${base64}${st}\n`);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function displayKitty(imagePath, options) {
|
|
227
|
+
try {
|
|
228
|
+
// Use kitten icat for Kitty terminal
|
|
229
|
+
await execAsync(`kitten icat --place ${options.maxWidth || 80}x${options.maxHeight || 24}@0x0 "${imagePath}"`);
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async function displaySixel(imagePath, options) {
|
|
237
|
+
try {
|
|
238
|
+
// Use img2sixel if available
|
|
239
|
+
await execAsync(`img2sixel -w ${options.maxWidth || 800} "${imagePath}"`);
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function displayAscii(imagePath, options) {
|
|
247
|
+
try {
|
|
248
|
+
// Generate simple ASCII representation using file info
|
|
249
|
+
const stats = await fs.promises.stat(imagePath);
|
|
250
|
+
const ext = path.extname(imagePath);
|
|
251
|
+
const name = path.basename(imagePath);
|
|
252
|
+
console.log('┌─────────────────────────────────────┐');
|
|
253
|
+
console.log(`│ 🖼️ ${name.slice(0, 33).padEnd(33)} │`);
|
|
254
|
+
console.log(`│ Format: ${ext.slice(1).toUpperCase().padEnd(24)} │`);
|
|
255
|
+
console.log(`│ Size: ${formatBytes(stats.size).padEnd(26)} │`);
|
|
256
|
+
console.log(`│ Path: ...${imagePath.slice(-22).padEnd(23)} │`);
|
|
257
|
+
console.log('└─────────────────────────────────────┘');
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function formatBytes(bytes) {
|
|
265
|
+
if (bytes < 1024)
|
|
266
|
+
return `${bytes} B`;
|
|
267
|
+
if (bytes < 1024 * 1024)
|
|
268
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
269
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
270
|
+
}
|
|
271
|
+
// ============================================================================
|
|
272
|
+
// System Viewer
|
|
273
|
+
// ============================================================================
|
|
274
|
+
async function openWithSystemViewer(filePath) {
|
|
275
|
+
const platform = process.platform;
|
|
276
|
+
try {
|
|
277
|
+
let command;
|
|
278
|
+
switch (platform) {
|
|
279
|
+
case 'darwin':
|
|
280
|
+
command = 'open';
|
|
281
|
+
break;
|
|
282
|
+
case 'win32':
|
|
283
|
+
command = 'start';
|
|
284
|
+
break;
|
|
285
|
+
default:
|
|
286
|
+
command = 'xdg-open';
|
|
287
|
+
}
|
|
288
|
+
await execAsync(`${command} "${filePath}"`);
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
console.error(`[Multimodal] Failed to open: ${error}`);
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// ============================================================================
|
|
297
|
+
// Main Handler
|
|
298
|
+
// ============================================================================
|
|
299
|
+
class MultimodalHandler {
|
|
300
|
+
options;
|
|
301
|
+
constructor(options = {}) {
|
|
302
|
+
this.options = {
|
|
303
|
+
maxWidth: 80,
|
|
304
|
+
maxHeight: 24,
|
|
305
|
+
autoOpen: false,
|
|
306
|
+
inline: true,
|
|
307
|
+
protocol: detectTerminalProtocol(),
|
|
308
|
+
...options,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
async handleResult(result) {
|
|
312
|
+
const media = extractMediaFromResult(result);
|
|
313
|
+
const multimodalResult = {
|
|
314
|
+
detected: media.length > 0,
|
|
315
|
+
media,
|
|
316
|
+
displayedInline: false,
|
|
317
|
+
openedExternally: false,
|
|
318
|
+
};
|
|
319
|
+
if (media.length === 0) {
|
|
320
|
+
return multimodalResult;
|
|
321
|
+
}
|
|
322
|
+
for (const item of media) {
|
|
323
|
+
if (item.type === 'image' && item.path) {
|
|
324
|
+
// Try inline display
|
|
325
|
+
if (this.options.inline) {
|
|
326
|
+
const displayed = await displayImageInline(item.path, this.options);
|
|
327
|
+
multimodalResult.displayedInline = multimodalResult.displayedInline || displayed;
|
|
328
|
+
}
|
|
329
|
+
// Auto-open if configured
|
|
330
|
+
if (this.options.autoOpen) {
|
|
331
|
+
const opened = await openWithSystemViewer(item.path);
|
|
332
|
+
multimodalResult.openedExternally = multimodalResult.openedExternally || opened;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return multimodalResult;
|
|
337
|
+
}
|
|
338
|
+
setOptions(options) {
|
|
339
|
+
Object.assign(this.options, options);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
exports.MultimodalHandler = MultimodalHandler;
|
|
343
|
+
// ============================================================================
|
|
344
|
+
// Singleton
|
|
345
|
+
// ============================================================================
|
|
346
|
+
let handlerInstance = null;
|
|
347
|
+
function getMultimodalHandler(options) {
|
|
348
|
+
if (!handlerInstance) {
|
|
349
|
+
handlerInstance = new MultimodalHandler(options);
|
|
350
|
+
}
|
|
351
|
+
return handlerInstance;
|
|
352
|
+
}
|
|
353
|
+
async function handleMultimodalResult(result) {
|
|
354
|
+
return getMultimodalHandler().handleResult(result);
|
|
355
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Genesis MCP Parallel DAG Executor
|
|
3
|
+
*
|
|
4
|
+
* Dependency-aware parallel execution of MCP tool calls.
|
|
5
|
+
* Analyzes call dependencies and maximizes parallelism.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Automatic dependency detection
|
|
9
|
+
* - Topological sort for execution order
|
|
10
|
+
* - Maximum parallelism within dependency constraints
|
|
11
|
+
* - Execution visualization
|
|
12
|
+
* - Cycle detection
|
|
13
|
+
*/
|
|
14
|
+
import { MCPServerName } from '../types.js';
|
|
15
|
+
import { MCPCallResult } from './index.js';
|
|
16
|
+
export interface DAGNode {
|
|
17
|
+
id: string;
|
|
18
|
+
server: MCPServerName;
|
|
19
|
+
tool: string;
|
|
20
|
+
params: Record<string, any> | ((results: Map<string, any>) => Record<string, any>);
|
|
21
|
+
dependsOn: string[];
|
|
22
|
+
priority?: number;
|
|
23
|
+
timeout?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface DAGExecutionResult {
|
|
26
|
+
success: boolean;
|
|
27
|
+
results: Map<string, MCPCallResult>;
|
|
28
|
+
errors: Map<string, Error>;
|
|
29
|
+
executionOrder: string[][];
|
|
30
|
+
totalLatency: number;
|
|
31
|
+
parallelismAchieved: number;
|
|
32
|
+
}
|
|
33
|
+
export interface DAGVisualization {
|
|
34
|
+
nodes: Array<{
|
|
35
|
+
id: string;
|
|
36
|
+
level: number;
|
|
37
|
+
dependsOn: string[];
|
|
38
|
+
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
39
|
+
}>;
|
|
40
|
+
levels: number;
|
|
41
|
+
criticalPath: string[];
|
|
42
|
+
}
|
|
43
|
+
export declare class ParallelDAGExecutor {
|
|
44
|
+
private mcpClient;
|
|
45
|
+
private maxConcurrency;
|
|
46
|
+
constructor(maxConcurrency?: number);
|
|
47
|
+
/**
|
|
48
|
+
* Execute a DAG of MCP calls with maximum parallelism
|
|
49
|
+
*/
|
|
50
|
+
execute(nodes: DAGNode[]): Promise<DAGExecutionResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Execute a single node
|
|
53
|
+
*/
|
|
54
|
+
private executeNode;
|
|
55
|
+
/**
|
|
56
|
+
* Build execution levels via topological sort
|
|
57
|
+
*/
|
|
58
|
+
private buildExecutionLevels;
|
|
59
|
+
/**
|
|
60
|
+
* Detect cycles using DFS
|
|
61
|
+
*/
|
|
62
|
+
private detectCycles;
|
|
63
|
+
/**
|
|
64
|
+
* Get visualization of DAG structure
|
|
65
|
+
*/
|
|
66
|
+
visualize(nodes: DAGNode[]): DAGVisualization;
|
|
67
|
+
/**
|
|
68
|
+
* Find the critical path (longest dependency chain)
|
|
69
|
+
*/
|
|
70
|
+
private findCriticalPath;
|
|
71
|
+
private chunkArray;
|
|
72
|
+
}
|
|
73
|
+
export declare class DAGBuilder {
|
|
74
|
+
private nodes;
|
|
75
|
+
/**
|
|
76
|
+
* Add a node to the DAG
|
|
77
|
+
*/
|
|
78
|
+
node(id: string, server: MCPServerName, tool: string, params: DAGNode['params'], options?: {
|
|
79
|
+
dependsOn?: string[];
|
|
80
|
+
priority?: number;
|
|
81
|
+
timeout?: number;
|
|
82
|
+
}): DAGBuilder;
|
|
83
|
+
/**
|
|
84
|
+
* Add dependency between nodes
|
|
85
|
+
*/
|
|
86
|
+
depend(nodeId: string, ...dependsOnIds: string[]): DAGBuilder;
|
|
87
|
+
/**
|
|
88
|
+
* Build and return the nodes
|
|
89
|
+
*/
|
|
90
|
+
build(): DAGNode[];
|
|
91
|
+
/**
|
|
92
|
+
* Execute the DAG
|
|
93
|
+
*/
|
|
94
|
+
execute(): Promise<DAGExecutionResult>;
|
|
95
|
+
}
|
|
96
|
+
export declare function getDAGExecutor(maxConcurrency?: number): ParallelDAGExecutor;
|
|
97
|
+
export declare function dag(): DAGBuilder;
|
|
98
|
+
/**
|
|
99
|
+
* Quick parallel execution of independent calls
|
|
100
|
+
*/
|
|
101
|
+
export declare function parallel(calls: Array<{
|
|
102
|
+
server: MCPServerName;
|
|
103
|
+
tool: string;
|
|
104
|
+
params: Record<string, any>;
|
|
105
|
+
}>): Promise<MCPCallResult[]>;
|
|
106
|
+
/**
|
|
107
|
+
* Sequential execution with result passing
|
|
108
|
+
*/
|
|
109
|
+
export declare function sequential(calls: Array<{
|
|
110
|
+
server: MCPServerName;
|
|
111
|
+
tool: string;
|
|
112
|
+
params: Record<string, any> | ((prev: any) => Record<string, any>);
|
|
113
|
+
}>): Promise<MCPCallResult[]>;
|