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,345 @@
1
+ "use strict";
2
+ /**
3
+ * Genesis MCP Streaming Results
4
+ *
5
+ * Real-time streaming of tool results with progress updates.
6
+ * Enables live feedback during long-running operations.
7
+ *
8
+ * Features:
9
+ * - Async iterator for results
10
+ * - Progress events (started, progress, chunk, complete, error)
11
+ * - Timeout handling with partial results
12
+ * - Buffered vs unbuffered modes
13
+ * - Event emitter integration
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ProgressReporter = exports.StreamingMCPWrapper = void 0;
17
+ exports.getStreamingWrapper = getStreamingWrapper;
18
+ exports.callStreaming = callStreaming;
19
+ const events_1 = require("events");
20
+ const index_js_1 = require("./index.js");
21
+ // ============================================================================
22
+ // Streaming Wrapper
23
+ // ============================================================================
24
+ class StreamingMCPWrapper extends events_1.EventEmitter {
25
+ mcpClient = (0, index_js_1.getMCPClient)();
26
+ activeStreams = new Map();
27
+ /**
28
+ * Call an MCP tool with streaming support
29
+ */
30
+ callStreaming(server, tool, params, options = {}) {
31
+ const streamId = `${server}.${tool}.${Date.now()}`;
32
+ const controller = new AbortController();
33
+ const startTime = Date.now();
34
+ this.activeStreams.set(streamId, { controller, startTime });
35
+ const events = new events_1.EventEmitter();
36
+ let cancelled = false;
37
+ let completed = false;
38
+ const chunks = [];
39
+ // Emit started event
40
+ const startEvent = {
41
+ type: 'started',
42
+ server,
43
+ tool,
44
+ timestamp: new Date(),
45
+ };
46
+ events.emit('started', startEvent);
47
+ this.emit('stream:started', startEvent);
48
+ // Set up progress interval
49
+ let progressInterval;
50
+ if (options.progressInterval) {
51
+ let progressCount = 0;
52
+ progressInterval = setInterval(() => {
53
+ if (!completed && !cancelled) {
54
+ progressCount++;
55
+ const progressEvent = {
56
+ type: 'progress',
57
+ server,
58
+ tool,
59
+ timestamp: new Date(),
60
+ progress: Math.min(95, progressCount * 10), // Synthetic progress
61
+ latency: Date.now() - startTime,
62
+ };
63
+ events.emit('progress', progressEvent);
64
+ this.emit('stream:progress', progressEvent);
65
+ }
66
+ }, options.progressInterval);
67
+ }
68
+ // Set up timeout
69
+ let timeoutHandle;
70
+ if (options.timeout) {
71
+ timeoutHandle = setTimeout(() => {
72
+ if (!completed) {
73
+ cancelled = true;
74
+ controller.abort();
75
+ const timeoutEvent = {
76
+ type: 'timeout',
77
+ server,
78
+ tool,
79
+ timestamp: new Date(),
80
+ latency: Date.now() - startTime,
81
+ data: options.partialOnTimeout && chunks.length > 0 ? chunks[chunks.length - 1] : undefined,
82
+ };
83
+ events.emit('timeout', timeoutEvent);
84
+ this.emit('stream:timeout', timeoutEvent);
85
+ }
86
+ }, options.timeout);
87
+ }
88
+ // Execute the call
89
+ const promise = (async () => {
90
+ try {
91
+ const result = await this.mcpClient.call(server, tool, params);
92
+ if (cancelled) {
93
+ throw new Error('Stream cancelled');
94
+ }
95
+ completed = true;
96
+ // Emit chunk for the result
97
+ const chunkEvent = {
98
+ type: 'chunk',
99
+ server,
100
+ tool,
101
+ timestamp: new Date(),
102
+ data: result.data,
103
+ latency: Date.now() - startTime,
104
+ };
105
+ events.emit('chunk', chunkEvent);
106
+ this.emit('stream:chunk', chunkEvent);
107
+ if (result.data) {
108
+ chunks.push(result.data);
109
+ }
110
+ // Emit complete event
111
+ const completeEvent = {
112
+ type: 'complete',
113
+ server,
114
+ tool,
115
+ timestamp: new Date(),
116
+ data: result.data,
117
+ progress: 100,
118
+ latency: Date.now() - startTime,
119
+ };
120
+ events.emit('complete', completeEvent);
121
+ this.emit('stream:complete', completeEvent);
122
+ return result;
123
+ }
124
+ catch (error) {
125
+ const err = error instanceof Error ? error : new Error(String(error));
126
+ const errorEvent = {
127
+ type: 'error',
128
+ server,
129
+ tool,
130
+ timestamp: new Date(),
131
+ error: err,
132
+ latency: Date.now() - startTime,
133
+ };
134
+ events.emit('error', errorEvent);
135
+ this.emit('stream:error', errorEvent);
136
+ return {
137
+ success: false,
138
+ error: err.message,
139
+ server,
140
+ tool,
141
+ mode: 'real',
142
+ latency: Date.now() - startTime,
143
+ timestamp: new Date(),
144
+ };
145
+ }
146
+ finally {
147
+ // Cleanup
148
+ if (progressInterval)
149
+ clearInterval(progressInterval);
150
+ if (timeoutHandle)
151
+ clearTimeout(timeoutHandle);
152
+ this.activeStreams.delete(streamId);
153
+ }
154
+ })();
155
+ // Create async iterator
156
+ const asyncIterator = async function* () {
157
+ const queue = [];
158
+ let resolveNext = null;
159
+ let done = false;
160
+ const push = (event) => {
161
+ if (resolveNext) {
162
+ resolveNext({ value: event, done: false });
163
+ resolveNext = null;
164
+ }
165
+ else {
166
+ queue.push(event);
167
+ }
168
+ };
169
+ events.on('started', push);
170
+ events.on('progress', push);
171
+ events.on('chunk', push);
172
+ events.on('complete', (e) => {
173
+ push(e);
174
+ done = true;
175
+ });
176
+ events.on('error', (e) => {
177
+ push(e);
178
+ done = true;
179
+ });
180
+ events.on('timeout', (e) => {
181
+ push(e);
182
+ done = true;
183
+ });
184
+ while (!done || queue.length > 0) {
185
+ if (queue.length > 0) {
186
+ yield queue.shift();
187
+ }
188
+ else {
189
+ const event = await new Promise((resolve) => {
190
+ resolveNext = (result) => resolve(result.value);
191
+ });
192
+ yield event;
193
+ }
194
+ }
195
+ };
196
+ return {
197
+ promise,
198
+ events,
199
+ [Symbol.asyncIterator]: asyncIterator,
200
+ cancel: () => {
201
+ cancelled = true;
202
+ controller.abort();
203
+ },
204
+ isRunning: () => !completed && !cancelled,
205
+ };
206
+ }
207
+ /**
208
+ * Call multiple tools with merged streaming
209
+ */
210
+ callParallelStreaming(calls, options = {}) {
211
+ const events = new events_1.EventEmitter();
212
+ const results = [];
213
+ let completedCount = 0;
214
+ let cancelled = false;
215
+ const startTime = Date.now();
216
+ const streams = calls.map((call) => this.callStreaming(call.server, call.tool, call.params, options));
217
+ // Forward all events
218
+ for (let i = 0; i < streams.length; i++) {
219
+ const stream = streams[i];
220
+ stream.events.on('started', (e) => events.emit('started', { ...e, index: i }));
221
+ stream.events.on('progress', (e) => events.emit('progress', { ...e, index: i }));
222
+ stream.events.on('chunk', (e) => {
223
+ if (e.data)
224
+ results[i] = e.data;
225
+ events.emit('chunk', { ...e, index: i });
226
+ });
227
+ stream.events.on('complete', (e) => {
228
+ completedCount++;
229
+ if (e.data)
230
+ results[i] = e.data;
231
+ events.emit('progress', {
232
+ ...e,
233
+ index: i,
234
+ progress: (completedCount / calls.length) * 100,
235
+ });
236
+ if (completedCount === calls.length) {
237
+ events.emit('complete', {
238
+ type: 'complete',
239
+ server: 'parallel',
240
+ tool: 'batch',
241
+ timestamp: new Date(),
242
+ data: results,
243
+ progress: 100,
244
+ latency: Date.now() - startTime,
245
+ });
246
+ }
247
+ });
248
+ stream.events.on('error', (e) => events.emit('error', { ...e, index: i }));
249
+ }
250
+ const promise = Promise.all(streams.map((s) => s.promise)).then((allResults) => ({
251
+ success: allResults.every((r) => r.success),
252
+ data: allResults.map((r) => r.data),
253
+ server: 'parallel',
254
+ tool: 'batch',
255
+ mode: 'real',
256
+ latency: Date.now() - startTime,
257
+ timestamp: new Date(),
258
+ }));
259
+ const asyncIterator = async function* () {
260
+ for (const stream of streams) {
261
+ for await (const event of stream) {
262
+ yield event;
263
+ }
264
+ }
265
+ };
266
+ return {
267
+ promise,
268
+ events,
269
+ [Symbol.asyncIterator]: asyncIterator,
270
+ cancel: () => {
271
+ cancelled = true;
272
+ streams.forEach((s) => s.cancel());
273
+ },
274
+ isRunning: () => !cancelled && completedCount < calls.length,
275
+ };
276
+ }
277
+ /**
278
+ * Get count of active streams
279
+ */
280
+ getActiveStreamCount() {
281
+ return this.activeStreams.size;
282
+ }
283
+ /**
284
+ * Cancel all active streams
285
+ */
286
+ cancelAll() {
287
+ for (const [id, { controller }] of this.activeStreams) {
288
+ controller.abort();
289
+ }
290
+ this.activeStreams.clear();
291
+ }
292
+ }
293
+ exports.StreamingMCPWrapper = StreamingMCPWrapper;
294
+ // ============================================================================
295
+ // Progress Reporter
296
+ // ============================================================================
297
+ class ProgressReporter {
298
+ startTime = 0;
299
+ lastUpdate = 0;
300
+ spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
301
+ spinnerIndex = 0;
302
+ start(label) {
303
+ this.startTime = Date.now();
304
+ this.lastUpdate = this.startTime;
305
+ process.stdout.write(`${this.getSpinner()} ${label}...`);
306
+ }
307
+ update(progress) {
308
+ const elapsed = Date.now() - this.startTime;
309
+ const progressStr = progress !== undefined ? ` ${progress.toFixed(0)}%` : '';
310
+ process.stdout.write(`\r${this.getSpinner()} Working...${progressStr} (${this.formatTime(elapsed)})`);
311
+ }
312
+ complete(label) {
313
+ const elapsed = Date.now() - this.startTime;
314
+ process.stdout.write(`\r✓ ${label} (${this.formatTime(elapsed)})\n`);
315
+ }
316
+ error(label) {
317
+ const elapsed = Date.now() - this.startTime;
318
+ process.stdout.write(`\r✗ ${label} (${this.formatTime(elapsed)})\n`);
319
+ }
320
+ getSpinner() {
321
+ this.spinnerIndex = (this.spinnerIndex + 1) % this.spinner.length;
322
+ return this.spinner[this.spinnerIndex];
323
+ }
324
+ formatTime(ms) {
325
+ if (ms < 1000)
326
+ return `${ms}ms`;
327
+ if (ms < 60000)
328
+ return `${(ms / 1000).toFixed(1)}s`;
329
+ return `${(ms / 60000).toFixed(1)}m`;
330
+ }
331
+ }
332
+ exports.ProgressReporter = ProgressReporter;
333
+ // ============================================================================
334
+ // Singleton
335
+ // ============================================================================
336
+ let streamingInstance = null;
337
+ function getStreamingWrapper() {
338
+ if (!streamingInstance) {
339
+ streamingInstance = new StreamingMCPWrapper();
340
+ }
341
+ return streamingInstance;
342
+ }
343
+ function callStreaming(server, tool, params, options) {
344
+ return getStreamingWrapper().callStreaming(server, tool, params, options);
345
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Genesis MCP Tool Chaining Framework
3
+ *
4
+ * Automatic orchestration of dependent tool calls.
5
+ * Enables workflows like: generate image → open it → edit it
6
+ *
7
+ * Features:
8
+ * - Declarative chain definitions
9
+ * - Automatic output → input mapping
10
+ * - Conditional branching
11
+ * - Error recovery and rollback
12
+ * - Chain execution history
13
+ */
14
+ import { MCPServerName } from '../types.js';
15
+ export interface ChainStep {
16
+ id: string;
17
+ server: MCPServerName;
18
+ tool: string;
19
+ params: Record<string, any> | ((ctx: ChainContext) => Record<string, any>);
20
+ transform?: (result: any) => any;
21
+ condition?: (ctx: ChainContext) => boolean;
22
+ onError?: 'stop' | 'skip' | 'retry' | ((error: Error, ctx: ChainContext) => ChainStep | null);
23
+ maxRetries?: number;
24
+ }
25
+ export interface ChainDefinition {
26
+ id: string;
27
+ name: string;
28
+ description?: string;
29
+ steps: ChainStep[];
30
+ initialContext?: Record<string, any>;
31
+ finalTransform?: (ctx: ChainContext) => any;
32
+ }
33
+ export interface ChainContext {
34
+ results: Map<string, any>;
35
+ currentStep: number;
36
+ errors: Array<{
37
+ stepId: string;
38
+ error: Error;
39
+ }>;
40
+ data: Record<string, any>;
41
+ startTime: Date;
42
+ log: ChainLogEntry[];
43
+ }
44
+ export interface ChainLogEntry {
45
+ timestamp: Date;
46
+ stepId: string;
47
+ action: 'start' | 'success' | 'error' | 'skip' | 'retry';
48
+ details?: any;
49
+ latency?: number;
50
+ }
51
+ export interface ChainResult {
52
+ success: boolean;
53
+ finalResult: any;
54
+ context: ChainContext;
55
+ totalLatency: number;
56
+ stepsExecuted: number;
57
+ stepsFailed: number;
58
+ }
59
+ export declare const CHAIN_TEMPLATES: Record<string, ChainDefinition>;
60
+ export declare class ToolChainExecutor {
61
+ private mcpClient;
62
+ execute(chain: ChainDefinition, initialData?: Record<string, any>): Promise<ChainResult>;
63
+ private executeStep;
64
+ private retryStep;
65
+ private log;
66
+ }
67
+ export declare class ChainBuilder {
68
+ private chain;
69
+ constructor(id: string, name: string);
70
+ description(desc: string): ChainBuilder;
71
+ initialContext(ctx: Record<string, any>): ChainBuilder;
72
+ step(step: ChainStep): ChainBuilder;
73
+ call(id: string, server: MCPServerName, tool: string, params: ChainStep['params']): ChainBuilder;
74
+ finalTransform(fn: (ctx: ChainContext) => any): ChainBuilder;
75
+ build(): ChainDefinition;
76
+ }
77
+ export declare function getChainExecutor(): ToolChainExecutor;
78
+ export declare function chain(id: string, name: string): ChainBuilder;
79
+ export declare function executeChain(chainOrId: ChainDefinition | string, data?: Record<string, any>): Promise<ChainResult>;