genesis-ai-cli 7.4.8 → 7.5.1

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.
@@ -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[]>;