tachibot-mcp 2.0.2
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/.env.example +260 -0
- package/CHANGELOG.md +54 -0
- package/CODE_OF_CONDUCT.md +56 -0
- package/CONTRIBUTING.md +54 -0
- package/Dockerfile +36 -0
- package/LICENSE +644 -0
- package/README.md +201 -0
- package/SECURITY.md +95 -0
- package/dist/personality/komaai-expressions.js +12 -0
- package/dist/profiles/balanced.json +33 -0
- package/dist/profiles/code_focus.json +33 -0
- package/dist/profiles/full.json +33 -0
- package/dist/profiles/minimal.json +33 -0
- package/dist/profiles/research_power.json +33 -0
- package/dist/scripts/build-profiles.js +46 -0
- package/dist/src/application/services/focus/FocusModeRegistry.js +46 -0
- package/dist/src/application/services/focus/FocusTool.service.js +109 -0
- package/dist/src/application/services/focus/ModeRegistry.js +46 -0
- package/dist/src/application/services/focus/modes/focus-deep.mode.js +27 -0
- package/dist/src/application/services/focus/modes/status.mode.js +50 -0
- package/dist/src/application/services/focus/modes/tachibot-status.mode.js +50 -0
- package/dist/src/collaborative-orchestrator.js +391 -0
- package/dist/src/config/model-constants.js +188 -0
- package/dist/src/config/model-defaults.js +57 -0
- package/dist/src/config/model-preferences.js +382 -0
- package/dist/src/config/timeout-config.js +130 -0
- package/dist/src/config.js +173 -0
- package/dist/src/domain/interfaces/IFocusMode.js +5 -0
- package/dist/src/domain/interfaces/IProvider.js +6 -0
- package/dist/src/domain/interfaces/ITool.js +5 -0
- package/dist/src/focus-deep.js +245 -0
- package/dist/src/infrastructure/ascii/art/robots.ascii.js +16 -0
- package/dist/src/mcp-client.js +90 -0
- package/dist/src/memory/index.js +17 -0
- package/dist/src/memory/memory-config.js +135 -0
- package/dist/src/memory/memory-interface.js +174 -0
- package/dist/src/memory/memory-manager.js +383 -0
- package/dist/src/memory/providers/devlog-provider.js +385 -0
- package/dist/src/memory/providers/hybrid-provider.js +399 -0
- package/dist/src/memory/providers/local-provider.js +388 -0
- package/dist/src/memory/providers/mem0-provider.js +337 -0
- package/dist/src/modes/architect.js +477 -0
- package/dist/src/modes/auditor.js +362 -0
- package/dist/src/modes/challenger.js +841 -0
- package/dist/src/modes/code-reviewer.js +382 -0
- package/dist/src/modes/commit-guardian.js +424 -0
- package/dist/src/modes/documentation-writer.js +572 -0
- package/dist/src/modes/scout.js +587 -0
- package/dist/src/modes/shared/helpers/challenger-helpers.js +454 -0
- package/dist/src/modes/shared/helpers/index.js +17 -0
- package/dist/src/modes/shared/helpers/scout-helpers.js +270 -0
- package/dist/src/modes/shared/helpers/verifier-helpers.js +332 -0
- package/dist/src/modes/test-architect.js +767 -0
- package/dist/src/modes/verifier.js +378 -0
- package/dist/src/monitoring/performance-monitor.js +435 -0
- package/dist/src/optimization/batch-executor.js +121 -0
- package/dist/src/optimization/context-pruner.js +196 -0
- package/dist/src/optimization/cost-monitor.js +338 -0
- package/dist/src/optimization/index.js +65 -0
- package/dist/src/optimization/model-router.js +264 -0
- package/dist/src/optimization/result-cache.js +114 -0
- package/dist/src/optimization/token-optimizer.js +257 -0
- package/dist/src/optimization/token-tracker.js +118 -0
- package/dist/src/orchestrator-instructions.js +128 -0
- package/dist/src/orchestrator-lite.js +139 -0
- package/dist/src/orchestrator.js +191 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionEngine.js +1 -0
- package/dist/src/orchestrators/collaborative/interfaces/IToolExecutionStrategy.js +5 -0
- package/dist/src/orchestrators/collaborative/interfaces/IVisualizationRenderer.js +1 -0
- package/dist/src/orchestrators/collaborative/registries/ModelProviderRegistry.js +95 -0
- package/dist/src/orchestrators/collaborative/registries/ToolAdapterRegistry.js +64 -0
- package/dist/src/orchestrators/collaborative/services/tool-execution/ToolExecutionService.js +502 -0
- package/dist/src/orchestrators/collaborative/services/visualization/VisualizationService.js +206 -0
- package/dist/src/orchestrators/collaborative/types/session-types.js +5 -0
- package/dist/src/profiles/balanced.js +37 -0
- package/dist/src/profiles/code_focus.js +37 -0
- package/dist/src/profiles/debug_intensive.js +59 -0
- package/dist/src/profiles/full.js +37 -0
- package/dist/src/profiles/minimal.js +37 -0
- package/dist/src/profiles/research_code.js +59 -0
- package/dist/src/profiles/research_power.js +37 -0
- package/dist/src/profiles/types.js +5 -0
- package/dist/src/profiles/workflow_builder.js +53 -0
- package/dist/src/prompt-engineer-lite.js +78 -0
- package/dist/src/prompt-engineer.js +399 -0
- package/dist/src/reasoning-chain.js +508 -0
- package/dist/src/sequential-thinking.js +291 -0
- package/dist/src/server-diagnostic.js +74 -0
- package/dist/src/server-raw.js +158 -0
- package/dist/src/server-simple.js +58 -0
- package/dist/src/server.js +514 -0
- package/dist/src/session/session-logger.js +617 -0
- package/dist/src/session/session-manager.js +571 -0
- package/dist/src/session/session-tools.js +400 -0
- package/dist/src/tools/advanced-modes.js +200 -0
- package/dist/src/tools/claude-integration.js +356 -0
- package/dist/src/tools/consolidated/ai-router.js +174 -0
- package/dist/src/tools/consolidated/ai-tool.js +48 -0
- package/dist/src/tools/consolidated/brainstorm-tool.js +87 -0
- package/dist/src/tools/consolidated/environment-detector.js +80 -0
- package/dist/src/tools/consolidated/index.js +50 -0
- package/dist/src/tools/consolidated/search-tool.js +110 -0
- package/dist/src/tools/consolidated/workflow-tool.js +238 -0
- package/dist/src/tools/gemini-tools.js +329 -0
- package/dist/src/tools/grok-enhanced.js +376 -0
- package/dist/src/tools/grok-tools.js +299 -0
- package/dist/src/tools/lmstudio-tools.js +223 -0
- package/dist/src/tools/openai-tools.js +498 -0
- package/dist/src/tools/openrouter-tools.js +317 -0
- package/dist/src/tools/optimized-wrapper.js +204 -0
- package/dist/src/tools/perplexity-tools.js +294 -0
- package/dist/src/tools/pingpong-tool.js +343 -0
- package/dist/src/tools/qwen-wrapper.js +74 -0
- package/dist/src/tools/tool-router.js +444 -0
- package/dist/src/tools/unified-ai-provider.js +260 -0
- package/dist/src/tools/workflow-runner.js +425 -0
- package/dist/src/tools/workflow-validator-tool.js +107 -0
- package/dist/src/types.js +23 -0
- package/dist/src/utils/input-validator.js +130 -0
- package/dist/src/utils/model-router.js +91 -0
- package/dist/src/utils/progress-stream.js +255 -0
- package/dist/src/utils/provider-router.js +88 -0
- package/dist/src/utils/smart-api-client.js +146 -0
- package/dist/src/utils/table-builder.js +218 -0
- package/dist/src/utils/timestamp-formatter.js +134 -0
- package/dist/src/utils/tool-compressor.js +257 -0
- package/dist/src/utils/tool-config.js +201 -0
- package/dist/src/validators/dependency-graph-validator.js +147 -0
- package/dist/src/validators/interpolation-validator.js +222 -0
- package/dist/src/validators/output-usage-validator.js +151 -0
- package/dist/src/validators/syntax-validator.js +102 -0
- package/dist/src/validators/tool-registry-validator.js +123 -0
- package/dist/src/validators/tool-types.js +97 -0
- package/dist/src/validators/types.js +8 -0
- package/dist/src/validators/workflow-validator.js +134 -0
- package/dist/src/visualizer-lite.js +42 -0
- package/dist/src/visualizer.js +179 -0
- package/dist/src/workflows/circuit-breaker.js +199 -0
- package/dist/src/workflows/custom-workflows.js +451 -0
- package/dist/src/workflows/engine/AutoSynthesizer.js +97 -0
- package/dist/src/workflows/engine/StepParameterResolver.js +74 -0
- package/dist/src/workflows/engine/VariableInterpolator.js +123 -0
- package/dist/src/workflows/engine/WorkflowDiscovery.js +125 -0
- package/dist/src/workflows/engine/WorkflowExecutionEngine.js +485 -0
- package/dist/src/workflows/engine/WorkflowExecutor.js +113 -0
- package/dist/src/workflows/engine/WorkflowFileManager.js +244 -0
- package/dist/src/workflows/engine/WorkflowHelpers.js +114 -0
- package/dist/src/workflows/engine/WorkflowOutputFormatter.js +83 -0
- package/dist/src/workflows/engine/events/WorkflowEventBus.js +132 -0
- package/dist/src/workflows/engine/events/interfaces/IEventBus.js +5 -0
- package/dist/src/workflows/engine/handlers/ErrorRecoveryHandler.js +162 -0
- package/dist/src/workflows/engine/handlers/PromptEnhancementHandler.js +115 -0
- package/dist/src/workflows/engine/handlers/SessionPersistenceHandler.js +167 -0
- package/dist/src/workflows/engine/handlers/StepExecutionHandler.js +231 -0
- package/dist/src/workflows/engine/handlers/ToolInvocationHandler.js +46 -0
- package/dist/src/workflows/engine/interfaces/IAutoSynthesizer.js +5 -0
- package/dist/src/workflows/engine/interfaces/IStepParameterResolver.js +5 -0
- package/dist/src/workflows/engine/interfaces/IVariableInterpolator.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowDiscovery.js +4 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowFileManager.js +5 -0
- package/dist/src/workflows/engine/interfaces/IWorkflowOutputFormatter.js +5 -0
- package/dist/src/workflows/engine/state/WorkflowStateMachine.js +194 -0
- package/dist/src/workflows/engine/state/interfaces/IStateMachine.js +17 -0
- package/dist/src/workflows/fallback-strategies.js +373 -0
- package/dist/src/workflows/message-queue.js +455 -0
- package/dist/src/workflows/model-router.js +189 -0
- package/dist/src/workflows/orchestrator-examples.js +174 -0
- package/dist/src/workflows/orchestrator-integration.js +200 -0
- package/dist/src/workflows/self-healing.js +524 -0
- package/dist/src/workflows/tool-mapper.js +407 -0
- package/dist/src/workflows/tool-orchestrator.js +796 -0
- package/dist/src/workflows/workflow-engine.js +573 -0
- package/dist/src/workflows/workflow-parser.js +283 -0
- package/dist/src/workflows/workflow-types.js +95 -0
- package/dist/src/workflows.js +568 -0
- package/dist/test-workflow-file-output.js +93 -0
- package/docs/API_KEYS.md +570 -0
- package/docs/CLAUDE_CODE_SETUP.md +181 -0
- package/docs/CLAUDE_DESKTOP_MANUAL.md +127 -0
- package/docs/CONFIGURATION.md +745 -0
- package/docs/FOCUS_MODES.md +240 -0
- package/docs/INSTALLATION_BOTH.md +145 -0
- package/docs/TERMS.md +352 -0
- package/docs/TOOLS_REFERENCE.md +1622 -0
- package/docs/TOOL_PARAMETERS.md +496 -0
- package/docs/TOOL_PROFILES.md +236 -0
- package/docs/WORKFLOWS.md +987 -0
- package/docs/WORKFLOW_OUTPUT.md +198 -0
- package/docs/WORKFLOW_PROGRESS_TRACKING.md +305 -0
- package/docs/workflows/design-brainstorm.md +335 -0
- package/package.json +97 -0
- package/profiles/balanced.json +37 -0
- package/profiles/code_focus.json +37 -0
- package/profiles/debug_intensive.json +34 -0
- package/profiles/full.json +37 -0
- package/profiles/minimal.json +37 -0
- package/profiles/research_power.json +37 -0
- package/profiles/workflow_builder.json +37 -0
- package/smithery.yaml +66 -0
- package/start.sh +8 -0
- package/tools.config.json +81 -0
- package/tsconfig.json +18 -0
- package/workflows/accessibility-code-audit.yaml +92 -0
- package/workflows/code-architecture-review.yaml +202 -0
- package/workflows/code-review.yaml +142 -0
- package/workflows/core/iterative-problem-solver.yaml +283 -0
- package/workflows/creative-brainstorm-yaml.yaml +215 -0
- package/workflows/pingpong.yaml +141 -0
- package/workflows/system/README.md +412 -0
- package/workflows/system/challenger.yaml +175 -0
- package/workflows/system/scout.yaml +164 -0
- package/workflows/system/verifier.yaml +133 -0
- package/workflows/ultra-creative-brainstorm.yaml +318 -0
- package/workflows/ux-research-flow.yaml +92 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool type system using TypeScript's infer and conditional types
|
|
3
|
+
* NO 'any' - uses 'unknown' where type is truly unknown
|
|
4
|
+
*/
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// TOOL REGISTRY - Single source of truth for all tools
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Known tool names - this is the ONLY place we list tools
|
|
10
|
+
* Validators check against this list
|
|
11
|
+
*/
|
|
12
|
+
export const KNOWN_TOOLS = [
|
|
13
|
+
// Core
|
|
14
|
+
'think',
|
|
15
|
+
'focus',
|
|
16
|
+
// Perplexity
|
|
17
|
+
'perplexity_ask',
|
|
18
|
+
'perplexity_reason',
|
|
19
|
+
'perplexity_research',
|
|
20
|
+
'perplexity_code_search',
|
|
21
|
+
// Grok
|
|
22
|
+
'grok_reason',
|
|
23
|
+
'grok_code',
|
|
24
|
+
'grok_debug',
|
|
25
|
+
'grok_brainstorm',
|
|
26
|
+
'grok_search',
|
|
27
|
+
'grok_heavy',
|
|
28
|
+
// OpenAI
|
|
29
|
+
'openai_brainstorm',
|
|
30
|
+
'openai_analyze',
|
|
31
|
+
'openai_reason',
|
|
32
|
+
'openai_compare',
|
|
33
|
+
'gpt5',
|
|
34
|
+
'gpt5_mini',
|
|
35
|
+
'gpt5_nano',
|
|
36
|
+
// Gemini
|
|
37
|
+
'gemini_query',
|
|
38
|
+
'gemini_brainstorm',
|
|
39
|
+
'gemini_analyze_code',
|
|
40
|
+
'gemini_analyze_text',
|
|
41
|
+
// Qwen
|
|
42
|
+
'qwen_coder',
|
|
43
|
+
'qwq_reason',
|
|
44
|
+
// Advanced modes
|
|
45
|
+
'verifier',
|
|
46
|
+
'challenger',
|
|
47
|
+
'scout',
|
|
48
|
+
'auditor',
|
|
49
|
+
'architect',
|
|
50
|
+
'commit_guardian',
|
|
51
|
+
// Meta tools
|
|
52
|
+
'code_reviewer',
|
|
53
|
+
'test_architect',
|
|
54
|
+
'documentation_writer',
|
|
55
|
+
// Workflow
|
|
56
|
+
'workflow',
|
|
57
|
+
'pingpong',
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* Type guard to check if string is a valid tool name
|
|
61
|
+
*/
|
|
62
|
+
export function isValidToolName(name) {
|
|
63
|
+
return KNOWN_TOOLS.includes(name);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Type guard to narrow Step to TypedStep<T>
|
|
67
|
+
*/
|
|
68
|
+
export function isTypedStep(step, toolName) {
|
|
69
|
+
return step.tool === toolName;
|
|
70
|
+
}
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// HELPER FUNCTIONS
|
|
73
|
+
// ============================================================================
|
|
74
|
+
/**
|
|
75
|
+
* Get all step names from workflow (for interpolation validation)
|
|
76
|
+
*/
|
|
77
|
+
export function getStepNames(workflow) {
|
|
78
|
+
return new Set(workflow.steps.map(s => s.name));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if a reference is a step name or variable
|
|
82
|
+
*/
|
|
83
|
+
export function isStepReference(reference, stepNames) {
|
|
84
|
+
// Remove .output suffix if present
|
|
85
|
+
const baseName = reference.includes('.')
|
|
86
|
+
? reference.split('.')[0]
|
|
87
|
+
: reference;
|
|
88
|
+
return stepNames.has(baseName);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Extract all interpolation references from a string
|
|
92
|
+
*/
|
|
93
|
+
export function extractInterpolations(text) {
|
|
94
|
+
const regex = /\$\{([^}]+)\}/g;
|
|
95
|
+
const matches = text.matchAll(regex);
|
|
96
|
+
return Array.from(matches, m => m[1]);
|
|
97
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow validator type definitions
|
|
3
|
+
*
|
|
4
|
+
* NOTE: This file now re-exports from tool-types.ts which uses proper
|
|
5
|
+
* TypeScript inference and conditional types without 'any'
|
|
6
|
+
*/
|
|
7
|
+
// Re-export all types from tool-types for backward compatibility
|
|
8
|
+
export * from './tool-types.js';
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main workflow validator - orchestrates all validation checks
|
|
3
|
+
*/
|
|
4
|
+
import { SyntaxValidator } from './syntax-validator.js';
|
|
5
|
+
import { InterpolationValidator } from './interpolation-validator.js';
|
|
6
|
+
import { ToolRegistryValidator } from './tool-registry-validator.js';
|
|
7
|
+
import { DependencyGraphValidator } from './dependency-graph-validator.js';
|
|
8
|
+
import { OutputUsageValidator } from './output-usage-validator.js';
|
|
9
|
+
import { getEnabledTools } from '../utils/tool-config.js';
|
|
10
|
+
export class WorkflowValidator {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.syntaxValidator = new SyntaxValidator();
|
|
13
|
+
this.interpolationValidator = new InterpolationValidator();
|
|
14
|
+
this.toolValidator = new ToolRegistryValidator();
|
|
15
|
+
this.dependencyValidator = new DependencyGraphValidator();
|
|
16
|
+
this.outputUsageValidator = new OutputUsageValidator();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validate a workflow from YAML/JSON content
|
|
20
|
+
*/
|
|
21
|
+
async validate(workflowContent, isJson = false, enabledToolsOverride) {
|
|
22
|
+
const allErrors = [];
|
|
23
|
+
// Step 1: Syntax validation
|
|
24
|
+
const syntaxResult = this.syntaxValidator.validate(workflowContent, isJson);
|
|
25
|
+
allErrors.push(...syntaxResult.errors);
|
|
26
|
+
// If syntax validation failed, can't proceed
|
|
27
|
+
if (!syntaxResult.valid || !syntaxResult.workflow) {
|
|
28
|
+
return {
|
|
29
|
+
valid: false,
|
|
30
|
+
errors: allErrors.filter(e => e.severity === 'error'),
|
|
31
|
+
warnings: allErrors.filter(e => e.severity === 'warning')
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Step 2: Build validation context
|
|
35
|
+
// If override provided, it contains ALL known tools
|
|
36
|
+
// Otherwise, get from config (which may be empty if config not loaded)
|
|
37
|
+
const allKnownTools = enabledToolsOverride || new Set(getEnabledTools());
|
|
38
|
+
const enabledTools = new Set(getEnabledTools());
|
|
39
|
+
const context = {
|
|
40
|
+
workflow: syntaxResult.workflow,
|
|
41
|
+
allKnownTools,
|
|
42
|
+
enabledTools: enabledTools.size > 0 ? enabledTools : allKnownTools, // Fallback to all if none enabled
|
|
43
|
+
sourceMap: new Map() // TODO: Implement source mapping for line numbers
|
|
44
|
+
};
|
|
45
|
+
// Step 3: Run all validators in parallel
|
|
46
|
+
const [interpolationErrors, toolErrors, dependencyErrors, outputUsageErrors] = await Promise.all([
|
|
47
|
+
Promise.resolve(this.interpolationValidator.validate(context)),
|
|
48
|
+
Promise.resolve(this.toolValidator.validate(context)),
|
|
49
|
+
Promise.resolve(this.dependencyValidator.validate(context)),
|
|
50
|
+
Promise.resolve(this.outputUsageValidator.validate(context))
|
|
51
|
+
]);
|
|
52
|
+
allErrors.push(...interpolationErrors);
|
|
53
|
+
allErrors.push(...toolErrors);
|
|
54
|
+
allErrors.push(...dependencyErrors);
|
|
55
|
+
allErrors.push(...outputUsageErrors);
|
|
56
|
+
// Separate errors and warnings
|
|
57
|
+
const errors = allErrors.filter(e => e.severity === 'error');
|
|
58
|
+
const warnings = allErrors.filter(e => e.severity === 'warning');
|
|
59
|
+
return {
|
|
60
|
+
valid: errors.length === 0,
|
|
61
|
+
errors,
|
|
62
|
+
warnings
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Validate a workflow from a file path
|
|
67
|
+
*/
|
|
68
|
+
async validateFile(filePath, enabledToolsOverride) {
|
|
69
|
+
const fs = await import('fs');
|
|
70
|
+
const path = await import('path');
|
|
71
|
+
try {
|
|
72
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
73
|
+
const isJson = path.extname(filePath).toLowerCase() === '.json';
|
|
74
|
+
return this.validate(content, isJson, enabledToolsOverride);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
valid: false,
|
|
79
|
+
errors: [{
|
|
80
|
+
type: 'syntax',
|
|
81
|
+
severity: 'error',
|
|
82
|
+
message: `Failed to read file: ${error.message}`,
|
|
83
|
+
path: '$'
|
|
84
|
+
}],
|
|
85
|
+
warnings: []
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Format validation results as human-readable text
|
|
91
|
+
*/
|
|
92
|
+
formatResults(result) {
|
|
93
|
+
const lines = [];
|
|
94
|
+
if (result.valid) {
|
|
95
|
+
lines.push('✅ Workflow validation passed!');
|
|
96
|
+
if (result.warnings.length > 0) {
|
|
97
|
+
lines.push(`\n⚠️ ${result.warnings.length} warning(s):\n`);
|
|
98
|
+
result.warnings.forEach((warning, i) => {
|
|
99
|
+
lines.push(this.formatError(warning, i + 1));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
lines.push(`❌ Workflow validation failed with ${result.errors.length} error(s)!\n`);
|
|
105
|
+
result.errors.forEach((error, i) => {
|
|
106
|
+
lines.push(this.formatError(error, i + 1));
|
|
107
|
+
});
|
|
108
|
+
if (result.warnings.length > 0) {
|
|
109
|
+
lines.push(`\n⚠️ ${result.warnings.length} warning(s):\n`);
|
|
110
|
+
result.warnings.forEach((warning, i) => {
|
|
111
|
+
lines.push(this.formatError(warning, i + 1));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return lines.join('\n');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Format a single error/warning
|
|
119
|
+
*/
|
|
120
|
+
formatError(error, index) {
|
|
121
|
+
const icon = error.severity === 'error' ? '❌' : '⚠️';
|
|
122
|
+
const location = error.line ? ` (line ${error.line}${error.column ? `:${error.column}` : ''})` : '';
|
|
123
|
+
const suggestion = error.suggestion ? `\n 💡 ${error.suggestion}` : '';
|
|
124
|
+
return `${icon} ${index}. [${error.type.toUpperCase()}]${location}\n ${error.message}\n Path: ${error.path}${suggestion}\n`;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Format validation results as JSON
|
|
128
|
+
*/
|
|
129
|
+
formatResultsJSON(result) {
|
|
130
|
+
return JSON.stringify(result, null, 2);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Export singleton instance
|
|
134
|
+
export const workflowValidator = new WorkflowValidator();
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ToolStatus } from './types.js';
|
|
2
|
+
import { PromptEngineerLite } from './prompt-engineer-lite.js';
|
|
3
|
+
export class WorkflowVisualizerLite {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.pe = new PromptEngineerLite();
|
|
6
|
+
this.faces = {
|
|
7
|
+
idle: [' ● ● ', ' ◡ '],
|
|
8
|
+
work: [' ◉ ◉ ', ' ~ '],
|
|
9
|
+
done: [' ★ ★ ', ' \\___//'],
|
|
10
|
+
err: [' x x ', ' ⌒ ']
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
getCompactDisplay(workflow, states) {
|
|
14
|
+
const tools = workflow.steps.map(s => s.tool.substring(0, 3));
|
|
15
|
+
const statuses = workflow.steps.map(s => {
|
|
16
|
+
const st = states.get(s.tool) || ToolStatus.IDLE;
|
|
17
|
+
return st === ToolStatus.COMPLETE ? '✓' :
|
|
18
|
+
st === ToolStatus.PROCESSING ? '◉' :
|
|
19
|
+
st === ToolStatus.ERROR ? '!' : '○';
|
|
20
|
+
});
|
|
21
|
+
const progress = Math.round((statuses.filter(s => s === '✓').length / tools.length) * 100);
|
|
22
|
+
const current = workflow.steps.find(s => states.get(s.tool) === ToolStatus.PROCESSING);
|
|
23
|
+
return `${workflow.name} [${statuses.join('→')}] ${progress}%${current ? ` | ${this.pe.getTechniqueDescription(current.promptTechnique)}` : ''}`;
|
|
24
|
+
}
|
|
25
|
+
async renderWorkflow(workflow, states) {
|
|
26
|
+
if (states.size === 0) {
|
|
27
|
+
workflow.steps.forEach(s => states.set(s.tool, ToolStatus.IDLE));
|
|
28
|
+
}
|
|
29
|
+
const display = this.getCompactDisplay(workflow, states);
|
|
30
|
+
console.error(`\n${display}\n`);
|
|
31
|
+
return display;
|
|
32
|
+
}
|
|
33
|
+
async updateProgress(tool, status) {
|
|
34
|
+
const emoji = { idle: '⏸️', processing: '⚙️', complete: '✅', error: '❌' };
|
|
35
|
+
console.error(`${emoji[status]} ${tool}: ${status}`);
|
|
36
|
+
}
|
|
37
|
+
async showCompletion(workflow, results) {
|
|
38
|
+
const dur = ((results[results.length - 1]?.duration || 0) / 1000).toFixed(1);
|
|
39
|
+
const face = this.faces.done;
|
|
40
|
+
console.error(`\n✨ ${workflow.name} Complete in ${dur}s\n @@@@@@@\n @ ${face[0]} @\n @ ${face[1]} @\n @@@@@@@`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { ToolStatus } from './types.js';
|
|
2
|
+
import { getStatusIndicator } from './workflows.js';
|
|
3
|
+
import { PromptEngineer } from './prompt-engineer.js';
|
|
4
|
+
export class WorkflowVisualizer {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.currentDisplay = '';
|
|
7
|
+
this.animationFrames = new Map();
|
|
8
|
+
this.promptEngineer = new PromptEngineer();
|
|
9
|
+
this.initializeAnimations();
|
|
10
|
+
}
|
|
11
|
+
initializeAnimations() {
|
|
12
|
+
// Processing animations for each tool
|
|
13
|
+
this.animationFrames.set('processing', [
|
|
14
|
+
'.', '..', '...', '..', '.'
|
|
15
|
+
]);
|
|
16
|
+
this.animationFrames.set('thinking', [
|
|
17
|
+
'~', '≈', '~', '∞', '~'
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
async renderWorkflow(workflow, toolStates) {
|
|
21
|
+
// Initialize states if empty
|
|
22
|
+
if (toolStates.size === 0) {
|
|
23
|
+
workflow.steps.forEach(step => {
|
|
24
|
+
toolStates.set(step.tool, ToolStatus.IDLE);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Build the display from the template
|
|
28
|
+
let display = workflow.visualTemplate;
|
|
29
|
+
// Replace placeholders with actual states
|
|
30
|
+
workflow.steps.forEach(step => {
|
|
31
|
+
const status = toolStates.get(step.tool) || ToolStatus.IDLE;
|
|
32
|
+
const indicator = getStatusIndicator(status);
|
|
33
|
+
// Replace status indicators in template
|
|
34
|
+
switch (step.tool) {
|
|
35
|
+
case 'gemini_brainstorm':
|
|
36
|
+
display = display.replace('{g}', indicator);
|
|
37
|
+
break;
|
|
38
|
+
case 'openai_brainstorm':
|
|
39
|
+
case 'openai_reason':
|
|
40
|
+
display = display.replace('{o}', indicator);
|
|
41
|
+
break;
|
|
42
|
+
case 'think':
|
|
43
|
+
display = display.replace('{t}', indicator);
|
|
44
|
+
break;
|
|
45
|
+
case 'perplexity_research':
|
|
46
|
+
display = display.replace('{p}', indicator);
|
|
47
|
+
break;
|
|
48
|
+
case 'reason':
|
|
49
|
+
display = display.replace('{r}', indicator);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
// Calculate overall progress
|
|
54
|
+
const completed = Array.from(toolStates.values()).filter(s => s === ToolStatus.COMPLETE).length;
|
|
55
|
+
const total = workflow.steps.length;
|
|
56
|
+
const progress = Math.round((completed / total) * 100);
|
|
57
|
+
// Create progress bar
|
|
58
|
+
const progressBar = this.createProgressBar(progress);
|
|
59
|
+
display = display.replace('{progress}', progressBar);
|
|
60
|
+
// Add current technique and activity
|
|
61
|
+
const currentTool = this.getCurrentTool(workflow, toolStates);
|
|
62
|
+
if (currentTool) {
|
|
63
|
+
const technique = this.promptEngineer.getTechniqueDescription(currentTool.promptTechnique);
|
|
64
|
+
display = display.replace('{technique}', technique);
|
|
65
|
+
display = display.replace('{current}', `Processing with ${currentTool.tool}...`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
display = display.replace('{technique}', 'Initializing workflow...');
|
|
69
|
+
display = display.replace('{current}', 'Starting orchestration...');
|
|
70
|
+
}
|
|
71
|
+
this.currentDisplay = display;
|
|
72
|
+
console.error('\n' + display + '\n');
|
|
73
|
+
return display;
|
|
74
|
+
}
|
|
75
|
+
async updateProgress(toolName, status) {
|
|
76
|
+
// This would normally update just the relevant part of the display
|
|
77
|
+
// For now, we'll log the update
|
|
78
|
+
const statusEmoji = {
|
|
79
|
+
[ToolStatus.IDLE]: '⏸️',
|
|
80
|
+
[ToolStatus.PROCESSING]: '⚙️',
|
|
81
|
+
[ToolStatus.COMPLETE]: '✅',
|
|
82
|
+
[ToolStatus.ERROR]: '❌'
|
|
83
|
+
};
|
|
84
|
+
console.error(`\n${statusEmoji[status]} ${toolName}: ${status}\n`);
|
|
85
|
+
// If processing, show animation
|
|
86
|
+
if (status === ToolStatus.PROCESSING) {
|
|
87
|
+
await this.animateProcessing(toolName);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async animateProcessing(toolName) {
|
|
91
|
+
// Show a simple animation for processing
|
|
92
|
+
const frames = this.animationFrames.get('processing') || ['.'];
|
|
93
|
+
console.error(`🔄 ${toolName} is thinking${frames[0]}`);
|
|
94
|
+
}
|
|
95
|
+
async showCompletion(workflow, results) {
|
|
96
|
+
const duration = results[results.length - 1]?.duration || 0;
|
|
97
|
+
const toolsUsed = results.map(r => r.tool).join(' → ');
|
|
98
|
+
const completion = `
|
|
99
|
+
╔═══════════════════════════════════════════════════╗
|
|
100
|
+
║ 🎉 Workflow Complete! 🎉 ║
|
|
101
|
+
╠═══════════════════════════════════════════════════╣
|
|
102
|
+
║ Workflow: ${workflow.name.padEnd(39)}║
|
|
103
|
+
║ Duration: ${(duration / 1000).toFixed(2)}s${' '.repeat(40 - (duration / 1000).toFixed(2).length - 1)}║
|
|
104
|
+
║ Tools: ${toolsUsed.substring(0, 42).padEnd(42)}║
|
|
105
|
+
╚═══════════════════════════════════════════════════╝
|
|
106
|
+
|
|
107
|
+
@@@@@@@@@
|
|
108
|
+
@ ● ● @
|
|
109
|
+
@ ◡ @ Mission accomplished!
|
|
110
|
+
@@@@@@@@@
|
|
111
|
+
`;
|
|
112
|
+
console.error(completion);
|
|
113
|
+
}
|
|
114
|
+
createProgressBar(percentage) {
|
|
115
|
+
const width = 20;
|
|
116
|
+
const filled = Math.round((percentage / 100) * width);
|
|
117
|
+
const empty = width - filled;
|
|
118
|
+
return `[${'='.repeat(filled)}${' '.repeat(empty)}] ${percentage}%`;
|
|
119
|
+
}
|
|
120
|
+
getCurrentTool(workflow, toolStates) {
|
|
121
|
+
// Find the currently processing tool
|
|
122
|
+
for (const step of workflow.steps) {
|
|
123
|
+
const status = toolStates.get(step.tool);
|
|
124
|
+
if (status === ToolStatus.PROCESSING) {
|
|
125
|
+
return step;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// If none processing, find the first incomplete
|
|
129
|
+
for (const step of workflow.steps) {
|
|
130
|
+
const status = toolStates.get(step.tool);
|
|
131
|
+
if (status !== ToolStatus.COMPLETE && status !== ToolStatus.ERROR) {
|
|
132
|
+
return step;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
// Special animations for different phases
|
|
138
|
+
async showCreativeExplosion() {
|
|
139
|
+
const explosion = `
|
|
140
|
+
✨ ★ ✨
|
|
141
|
+
★ 💡 ★
|
|
142
|
+
✨ 🎨 ✨
|
|
143
|
+
★ ★
|
|
144
|
+
✨
|
|
145
|
+
Creative breakthrough!
|
|
146
|
+
`;
|
|
147
|
+
console.error(explosion);
|
|
148
|
+
}
|
|
149
|
+
async showResearchDiscovery() {
|
|
150
|
+
const discovery = `
|
|
151
|
+
📚 → 🔍 → 💎
|
|
152
|
+
|
|
153
|
+
Found valuable insights!
|
|
154
|
+
`;
|
|
155
|
+
console.error(discovery);
|
|
156
|
+
}
|
|
157
|
+
async showProblemSolved() {
|
|
158
|
+
const solved = `
|
|
159
|
+
🧩 + 🧩 + 🧩 = ✅
|
|
160
|
+
|
|
161
|
+
Problem decomposed and solved!
|
|
162
|
+
`;
|
|
163
|
+
console.error(solved);
|
|
164
|
+
}
|
|
165
|
+
async showSynthesisComplete() {
|
|
166
|
+
const synthesis = `
|
|
167
|
+
🎯 All perspectives integrated!
|
|
168
|
+
|
|
169
|
+
╱│╲
|
|
170
|
+
─●─ Unified insight achieved
|
|
171
|
+
╲│╱
|
|
172
|
+
`;
|
|
173
|
+
console.error(synthesis);
|
|
174
|
+
}
|
|
175
|
+
// Get current display for external use
|
|
176
|
+
getCurrentDisplay() {
|
|
177
|
+
return this.currentDisplay;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export var CircuitState;
|
|
3
|
+
(function (CircuitState) {
|
|
4
|
+
CircuitState["CLOSED"] = "CLOSED";
|
|
5
|
+
CircuitState["OPEN"] = "OPEN";
|
|
6
|
+
CircuitState["HALF_OPEN"] = "HALF_OPEN"; // Testing if service recovered
|
|
7
|
+
})(CircuitState || (CircuitState = {}));
|
|
8
|
+
export class CircuitBreaker extends EventEmitter {
|
|
9
|
+
constructor(stepId, options) {
|
|
10
|
+
super();
|
|
11
|
+
this.stepId = stepId;
|
|
12
|
+
this.options = options;
|
|
13
|
+
this.state = CircuitState.CLOSED;
|
|
14
|
+
this.failures = 0;
|
|
15
|
+
this.successes = 0;
|
|
16
|
+
this.lastFailureTime = 0;
|
|
17
|
+
this.lastSuccessTime = 0;
|
|
18
|
+
this.totalRequests = 0;
|
|
19
|
+
this.totalFailures = 0;
|
|
20
|
+
this.startTime = Date.now();
|
|
21
|
+
this.nextAttempt = 0;
|
|
22
|
+
this.setMaxListeners(20); // Allow multiple listeners
|
|
23
|
+
}
|
|
24
|
+
async execute(operation) {
|
|
25
|
+
this.totalRequests++;
|
|
26
|
+
if (this.state === CircuitState.OPEN) {
|
|
27
|
+
if (this.shouldAttemptReset()) {
|
|
28
|
+
this.state = CircuitState.HALF_OPEN;
|
|
29
|
+
this.successes = 0;
|
|
30
|
+
this.emit('half-open', { stepId: this.stepId });
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
this.emit('fallback', { stepId: this.stepId, reason: 'circuit-open' });
|
|
34
|
+
return this.executeFallback();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const result = await operation();
|
|
39
|
+
this.onSuccess();
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
this.onFailure(error);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
onSuccess() {
|
|
48
|
+
this.failures = 0;
|
|
49
|
+
this.successes++;
|
|
50
|
+
this.lastSuccessTime = Date.now();
|
|
51
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
52
|
+
if (this.successes >= this.options.successThreshold) {
|
|
53
|
+
this.state = CircuitState.CLOSED;
|
|
54
|
+
this.emit('closed', { stepId: this.stepId });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
this.emit('success', { stepId: this.stepId, state: this.state });
|
|
58
|
+
}
|
|
59
|
+
onFailure(error) {
|
|
60
|
+
this.failures++;
|
|
61
|
+
this.totalFailures++;
|
|
62
|
+
this.lastFailureTime = Date.now();
|
|
63
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
64
|
+
this.state = CircuitState.OPEN;
|
|
65
|
+
this.nextAttempt = Date.now() + this.options.recoveryTimeout;
|
|
66
|
+
this.emit('opened', { stepId: this.stepId, reason: 'half-open-failure' });
|
|
67
|
+
}
|
|
68
|
+
else if (this.state === CircuitState.CLOSED) {
|
|
69
|
+
if (this.failures >= this.options.failureThreshold) {
|
|
70
|
+
this.state = CircuitState.OPEN;
|
|
71
|
+
this.nextAttempt = Date.now() + this.options.recoveryTimeout;
|
|
72
|
+
this.emit('opened', { stepId: this.stepId, reason: 'failure-threshold' });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
this.emit('failure', { stepId: this.stepId, error: error.message, state: this.state });
|
|
76
|
+
}
|
|
77
|
+
shouldAttemptReset() {
|
|
78
|
+
return Date.now() >= this.nextAttempt;
|
|
79
|
+
}
|
|
80
|
+
async executeFallback() {
|
|
81
|
+
if (this.options.fallback) {
|
|
82
|
+
try {
|
|
83
|
+
const result = await this.options.fallback();
|
|
84
|
+
this.emit('fallback-success', { stepId: this.stepId });
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
this.emit('fallback-failure', { stepId: this.stepId, error: error.message });
|
|
89
|
+
throw new Error(`Circuit breaker open for ${this.stepId} and fallback failed: ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
throw new Error(`Circuit breaker open for ${this.stepId} and no fallback configured`);
|
|
93
|
+
}
|
|
94
|
+
getStats() {
|
|
95
|
+
return {
|
|
96
|
+
state: this.state,
|
|
97
|
+
failures: this.failures,
|
|
98
|
+
successes: this.successes,
|
|
99
|
+
lastFailure: this.lastFailureTime,
|
|
100
|
+
lastSuccess: this.lastSuccessTime,
|
|
101
|
+
totalRequests: this.totalRequests,
|
|
102
|
+
totalFailures: this.totalFailures,
|
|
103
|
+
uptime: Date.now() - this.startTime
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Manual control methods
|
|
107
|
+
forceOpen() {
|
|
108
|
+
this.state = CircuitState.OPEN;
|
|
109
|
+
this.nextAttempt = Date.now() + this.options.recoveryTimeout;
|
|
110
|
+
this.emit('force-opened', { stepId: this.stepId });
|
|
111
|
+
}
|
|
112
|
+
forceClose() {
|
|
113
|
+
this.state = CircuitState.CLOSED;
|
|
114
|
+
this.failures = 0;
|
|
115
|
+
this.successes = 0;
|
|
116
|
+
this.emit('force-closed', { stepId: this.stepId });
|
|
117
|
+
}
|
|
118
|
+
forceHalfOpen() {
|
|
119
|
+
this.state = CircuitState.HALF_OPEN;
|
|
120
|
+
this.successes = 0;
|
|
121
|
+
this.emit('force-half-open', { stepId: this.stepId });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export class CircuitBreakerManager extends EventEmitter {
|
|
125
|
+
constructor() {
|
|
126
|
+
super(...arguments);
|
|
127
|
+
this.breakers = new Map();
|
|
128
|
+
this.defaultOptions = {
|
|
129
|
+
failureThreshold: 5,
|
|
130
|
+
recoveryTimeout: 30000, // 30 seconds
|
|
131
|
+
successThreshold: 2,
|
|
132
|
+
monitoringWindow: 60000, // 1 minute
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
getOrCreateCircuitBreaker(stepId, options) {
|
|
136
|
+
if (!this.breakers.has(stepId)) {
|
|
137
|
+
const finalOptions = { ...this.defaultOptions, ...options };
|
|
138
|
+
const breaker = new CircuitBreaker(stepId, finalOptions);
|
|
139
|
+
// Proxy all events to the manager
|
|
140
|
+
breaker.on('opened', (data) => this.emit('breaker-opened', data));
|
|
141
|
+
breaker.on('closed', (data) => this.emit('breaker-closed', data));
|
|
142
|
+
breaker.on('half-open', (data) => this.emit('breaker-half-open', data));
|
|
143
|
+
breaker.on('failure', (data) => this.emit('breaker-failure', data));
|
|
144
|
+
breaker.on('success', (data) => this.emit('breaker-success', data));
|
|
145
|
+
breaker.on('fallback', (data) => this.emit('breaker-fallback', data));
|
|
146
|
+
this.breakers.set(stepId, breaker);
|
|
147
|
+
}
|
|
148
|
+
return this.breakers.get(stepId);
|
|
149
|
+
}
|
|
150
|
+
getAllStats() {
|
|
151
|
+
const stats = {};
|
|
152
|
+
for (const [stepId, breaker] of this.breakers.entries()) {
|
|
153
|
+
stats[stepId] = breaker.getStats();
|
|
154
|
+
}
|
|
155
|
+
return stats;
|
|
156
|
+
}
|
|
157
|
+
getHealthReport() {
|
|
158
|
+
const stats = this.getAllStats();
|
|
159
|
+
const total = Object.keys(stats).length;
|
|
160
|
+
let open = 0;
|
|
161
|
+
let halfOpen = 0;
|
|
162
|
+
let closed = 0;
|
|
163
|
+
for (const stat of Object.values(stats)) {
|
|
164
|
+
switch (stat.state) {
|
|
165
|
+
case CircuitState.OPEN:
|
|
166
|
+
open++;
|
|
167
|
+
break;
|
|
168
|
+
case CircuitState.HALF_OPEN:
|
|
169
|
+
halfOpen++;
|
|
170
|
+
break;
|
|
171
|
+
case CircuitState.CLOSED:
|
|
172
|
+
closed++;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const healthScore = total === 0 ? 1.0 : closed / total;
|
|
177
|
+
return {
|
|
178
|
+
totalBreakers: total,
|
|
179
|
+
openBreakers: open,
|
|
180
|
+
halfOpenBreakers: halfOpen,
|
|
181
|
+
closedBreakers: closed,
|
|
182
|
+
healthScore
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// Cleanup unused breakers
|
|
186
|
+
cleanup() {
|
|
187
|
+
const now = Date.now();
|
|
188
|
+
const maxIdle = 300000; // 5 minutes
|
|
189
|
+
for (const [stepId, breaker] of this.breakers.entries()) {
|
|
190
|
+
const stats = breaker.getStats();
|
|
191
|
+
const lastActivity = Math.max(stats.lastSuccess, stats.lastFailure);
|
|
192
|
+
if (lastActivity > 0 && (now - lastActivity) > maxIdle) {
|
|
193
|
+
breaker.removeAllListeners();
|
|
194
|
+
this.breakers.delete(stepId);
|
|
195
|
+
this.emit('breaker-cleanup', { stepId });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|