mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-20 01:13:07 +00:00
feat: Add workflow activation/deactivation via diff operations
Implements workflow activation and deactivation as diff operations in n8n_update_partial_workflow tool, following the pattern of other configuration operations. Changes: - Add activateWorkflow/deactivateWorkflow API methods - Add operation types to diff engine - Update tool documentation - Remove activation limitation Resolves #399 Credits: ArtemisAI, cmj-hub for investigation and initial implementation Conceived by Romuald Członkowski - www.aiadvisors.pl/en
This commit is contained in:
105
CHANGELOG.md
105
CHANGELOG.md
@@ -7,6 +7,111 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [2.22.11] - 2025-01-06
|
||||||
|
|
||||||
|
### ✨ New Features
|
||||||
|
|
||||||
|
**Issue #399: Workflow Activation via Diff Operations**
|
||||||
|
|
||||||
|
Added workflow activation and deactivation as diff operations in `n8n_update_partial_workflow`, using n8n's dedicated API endpoints.
|
||||||
|
|
||||||
|
#### Problem
|
||||||
|
|
||||||
|
The n8n API provides dedicated `POST /workflows/{id}/activate` and `POST /workflows/{id}/deactivate` endpoints, but these were not accessible through n8n-mcp. Users could not programmatically control workflow activation status, forcing manual activation through the n8n UI.
|
||||||
|
|
||||||
|
#### Solution
|
||||||
|
|
||||||
|
Implemented activation/deactivation as diff operations, following the established pattern of metadata operations like `updateSettings` and `updateName`. This keeps the tool count manageable (40 tools, not 42) and provides a consistent interface.
|
||||||
|
|
||||||
|
#### Changes
|
||||||
|
|
||||||
|
**API Client** (`src/services/n8n-api-client.ts`):
|
||||||
|
- Added `activateWorkflow(id: string): Promise<Workflow>` method
|
||||||
|
- Added `deactivateWorkflow(id: string): Promise<Workflow>` method
|
||||||
|
- Both use POST requests to dedicated n8n API endpoints
|
||||||
|
|
||||||
|
**Diff Engine Types** (`src/types/workflow-diff.ts`):
|
||||||
|
- Added `ActivateWorkflowOperation` interface
|
||||||
|
- Added `DeactivateWorkflowOperation` interface
|
||||||
|
- Added `shouldActivate` and `shouldDeactivate` flags to `WorkflowDiffResult`
|
||||||
|
- Increased supported operations from 15 to 17
|
||||||
|
|
||||||
|
**Diff Engine** (`src/services/workflow-diff-engine.ts`):
|
||||||
|
- Added validation for activation (requires activatable triggers)
|
||||||
|
- Added operation application logic
|
||||||
|
- Transfers activation intent from workflow object to result
|
||||||
|
- Validates workflow has activatable triggers (webhook, schedule, etc.)
|
||||||
|
- Rejects workflows with only `executeWorkflowTrigger` (cannot activate)
|
||||||
|
|
||||||
|
**Handler** (`src/mcp/handlers-workflow-diff.ts`):
|
||||||
|
- Checks `shouldActivate` and `shouldDeactivate` flags after workflow update
|
||||||
|
- Calls appropriate API methods
|
||||||
|
- Includes activation status in response message and details
|
||||||
|
- Handles activation/deactivation errors gracefully
|
||||||
|
|
||||||
|
**Documentation** (`src/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.ts`):
|
||||||
|
- Updated operation count from 15 to 17
|
||||||
|
- Added "Workflow Activation Operations" section
|
||||||
|
- Added activation tip to essentials
|
||||||
|
|
||||||
|
**Tool Registration** (`src/mcp/handlers-n8n-manager.ts`):
|
||||||
|
- Removed "Cannot activate/deactivate workflows via API" from limitations
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Activate workflow
|
||||||
|
n8n_update_partial_workflow({
|
||||||
|
id: "workflow_id",
|
||||||
|
operations: [{
|
||||||
|
type: "activateWorkflow"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Deactivate workflow
|
||||||
|
n8n_update_partial_workflow({
|
||||||
|
id: "workflow_id",
|
||||||
|
operations: [{
|
||||||
|
type: "deactivateWorkflow"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Combine with other operations
|
||||||
|
n8n_update_partial_workflow({
|
||||||
|
id: "workflow_id",
|
||||||
|
operations: [
|
||||||
|
{type: "updateNode", nodeId: "abc", updates: {name: "Updated"}},
|
||||||
|
{type: "activateWorkflow"}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Validation
|
||||||
|
|
||||||
|
- **Activation**: Requires at least one enabled activatable trigger node
|
||||||
|
- **Deactivation**: Always valid
|
||||||
|
- **Error Handling**: Clear messages when activation fails due to missing triggers
|
||||||
|
- **Trigger Detection**: Uses `isActivatableTrigger()` utility (Issue #351 compliance)
|
||||||
|
|
||||||
|
#### Benefits
|
||||||
|
|
||||||
|
- ✅ Consistent with existing architecture (metadata operations pattern)
|
||||||
|
- ✅ Keeps tool count at 40 (not 42)
|
||||||
|
- ✅ Atomic operations - activation happens after workflow update
|
||||||
|
- ✅ Proper validation - prevents activation without triggers
|
||||||
|
- ✅ Clear error messages - guides users on trigger requirements
|
||||||
|
- ✅ Works with other operations - can update and activate in one call
|
||||||
|
|
||||||
|
#### Credits
|
||||||
|
|
||||||
|
- **@ArtemisAI** - Original investigation and API endpoint discovery
|
||||||
|
- **@cmj-hub** - Implementation attempt and PR contribution
|
||||||
|
- Architectural guidance from project maintainer
|
||||||
|
|
||||||
|
Resolves #399
|
||||||
|
|
||||||
|
Conceived by Romuald Członkowski - [www.aiadvisors.pl/en](https://www.aiadvisors.pl/en)
|
||||||
|
|
||||||
## [2.22.10] - 2025-11-04
|
## [2.22.10] - 2025-11-04
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|||||||
BIN
data/nodes.db
BIN
data/nodes.db
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-mcp",
|
"name": "n8n-mcp",
|
||||||
"version": "2.22.10",
|
"version": "2.22.11",
|
||||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-mcp-runtime",
|
"name": "n8n-mcp-runtime",
|
||||||
"version": "2.22.10",
|
"version": "2.22.11",
|
||||||
"description": "n8n MCP Server Runtime Dependencies Only",
|
"description": "n8n MCP Server Runtime Dependencies Only",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1561,7 +1561,6 @@ export async function handleListAvailableTools(context?: InstanceContext): Promi
|
|||||||
maxRetries: config.maxRetries
|
maxRetries: config.maxRetries
|
||||||
} : null,
|
} : null,
|
||||||
limitations: [
|
limitations: [
|
||||||
'Cannot activate/deactivate workflows via API',
|
|
||||||
'Cannot execute workflows directly (must use webhooks)',
|
'Cannot execute workflows directly (must use webhooks)',
|
||||||
'Cannot stop running executions',
|
'Cannot stop running executions',
|
||||||
'Tags and credentials have limited API support'
|
'Tags and credentials have limited API support'
|
||||||
|
|||||||
@@ -245,15 +245,52 @@ export async function handleUpdatePartialWorkflow(
|
|||||||
// Update workflow via API
|
// Update workflow via API
|
||||||
try {
|
try {
|
||||||
const updatedWorkflow = await client.updateWorkflow(input.id, diffResult.workflow!);
|
const updatedWorkflow = await client.updateWorkflow(input.id, diffResult.workflow!);
|
||||||
|
|
||||||
|
// Handle activation/deactivation if requested
|
||||||
|
let finalWorkflow = updatedWorkflow;
|
||||||
|
let activationMessage = '';
|
||||||
|
|
||||||
|
if (diffResult.shouldActivate) {
|
||||||
|
try {
|
||||||
|
finalWorkflow = await client.activateWorkflow(input.id);
|
||||||
|
activationMessage = ' Workflow activated.';
|
||||||
|
} catch (activationError) {
|
||||||
|
logger.error('Failed to activate workflow after update', activationError);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Workflow updated successfully but activation failed',
|
||||||
|
details: {
|
||||||
|
workflowUpdated: true,
|
||||||
|
activationError: activationError instanceof Error ? activationError.message : 'Unknown error'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (diffResult.shouldDeactivate) {
|
||||||
|
try {
|
||||||
|
finalWorkflow = await client.deactivateWorkflow(input.id);
|
||||||
|
activationMessage = ' Workflow deactivated.';
|
||||||
|
} catch (deactivationError) {
|
||||||
|
logger.error('Failed to deactivate workflow after update', deactivationError);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: 'Workflow updated successfully but deactivation failed',
|
||||||
|
details: {
|
||||||
|
workflowUpdated: true,
|
||||||
|
deactivationError: deactivationError instanceof Error ? deactivationError.message : 'Unknown error'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: updatedWorkflow,
|
data: finalWorkflow,
|
||||||
message: `Workflow "${updatedWorkflow.name}" updated successfully. Applied ${diffResult.operationsApplied} operations.`,
|
message: `Workflow "${finalWorkflow.name}" updated successfully. Applied ${diffResult.operationsApplied} operations.${activationMessage}`,
|
||||||
details: {
|
details: {
|
||||||
operationsApplied: diffResult.operationsApplied,
|
operationsApplied: diffResult.operationsApplied,
|
||||||
workflowId: updatedWorkflow.id,
|
workflowId: finalWorkflow.id,
|
||||||
workflowName: updatedWorkflow.name,
|
workflowName: finalWorkflow.name,
|
||||||
|
active: finalWorkflow.active,
|
||||||
applied: diffResult.applied,
|
applied: diffResult.applied,
|
||||||
failed: diffResult.failed,
|
failed: diffResult.failed,
|
||||||
errors: diffResult.errors,
|
errors: diffResult.errors,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
|
|||||||
name: 'n8n_update_partial_workflow',
|
name: 'n8n_update_partial_workflow',
|
||||||
category: 'workflow_management',
|
category: 'workflow_management',
|
||||||
essentials: {
|
essentials: {
|
||||||
description: 'Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, rewireConnection, cleanStaleConnections, replaceConnections, updateSettings, updateName, add/removeTag. Supports smart parameters (branch, case) for multi-output nodes. Full support for AI connections (ai_languageModel, ai_tool, ai_memory, ai_embedding, ai_vectorStore, ai_document, ai_textSplitter, ai_outputParser).',
|
description: 'Update workflow incrementally with diff operations. Types: addNode, removeNode, updateNode, moveNode, enable/disableNode, addConnection, removeConnection, rewireConnection, cleanStaleConnections, replaceConnections, updateSettings, updateName, add/removeTag, activateWorkflow, deactivateWorkflow. Supports smart parameters (branch, case) for multi-output nodes. Full support for AI connections (ai_languageModel, ai_tool, ai_memory, ai_embedding, ai_vectorStore, ai_document, ai_textSplitter, ai_outputParser).',
|
||||||
keyParameters: ['id', 'operations', 'continueOnError'],
|
keyParameters: ['id', 'operations', 'continueOnError'],
|
||||||
example: 'n8n_update_partial_workflow({id: "wf_123", operations: [{type: "rewireConnection", source: "IF", from: "Old", to: "New", branch: "true"}]})',
|
example: 'n8n_update_partial_workflow({id: "wf_123", operations: [{type: "rewireConnection", source: "IF", from: "Old", to: "New", branch: "true"}]})',
|
||||||
performance: 'Fast (50-200ms)',
|
performance: 'Fast (50-200ms)',
|
||||||
@@ -19,11 +19,12 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
|
|||||||
'For AI connections, specify sourceOutput type (ai_languageModel, ai_tool, etc.)',
|
'For AI connections, specify sourceOutput type (ai_languageModel, ai_tool, etc.)',
|
||||||
'Batch AI component connections for atomic updates',
|
'Batch AI component connections for atomic updates',
|
||||||
'Auto-sanitization: ALL nodes auto-fixed during updates (operator structures, missing metadata)',
|
'Auto-sanitization: ALL nodes auto-fixed during updates (operator structures, missing metadata)',
|
||||||
'Node renames automatically update all connection references - no manual connection operations needed'
|
'Node renames automatically update all connection references - no manual connection operations needed',
|
||||||
|
'Activate/deactivate workflows: Use activateWorkflow/deactivateWorkflow operations (requires activatable triggers like webhook/schedule)'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
full: {
|
full: {
|
||||||
description: `Updates workflows using surgical diff operations instead of full replacement. Supports 15 operation types for precise modifications. Operations are validated and applied atomically by default - all succeed or none are applied.
|
description: `Updates workflows using surgical diff operations instead of full replacement. Supports 17 operation types for precise modifications. Operations are validated and applied atomically by default - all succeed or none are applied.
|
||||||
|
|
||||||
## Available Operations:
|
## Available Operations:
|
||||||
|
|
||||||
@@ -48,6 +49,10 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
|
|||||||
- **addTag**: Add a workflow tag
|
- **addTag**: Add a workflow tag
|
||||||
- **removeTag**: Remove a workflow tag
|
- **removeTag**: Remove a workflow tag
|
||||||
|
|
||||||
|
### Workflow Activation Operations (2 types):
|
||||||
|
- **activateWorkflow**: Activate the workflow to enable automatic execution via triggers
|
||||||
|
- **deactivateWorkflow**: Deactivate the workflow to prevent automatic execution
|
||||||
|
|
||||||
## Smart Parameters for Multi-Output Nodes
|
## Smart Parameters for Multi-Output Nodes
|
||||||
|
|
||||||
For **IF nodes**, use semantic 'branch' parameter instead of technical sourceIndex:
|
For **IF nodes**, use semantic 'branch' parameter instead of technical sourceIndex:
|
||||||
|
|||||||
@@ -170,6 +170,24 @@ export class N8nApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async activateWorkflow(id: string): Promise<Workflow> {
|
||||||
|
try {
|
||||||
|
const response = await this.client.post(`/workflows/${id}/activate`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
throw handleN8nApiError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deactivateWorkflow(id: string): Promise<Workflow> {
|
||||||
|
try {
|
||||||
|
const response = await this.client.post(`/workflows/${id}/deactivate`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
throw handleN8nApiError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists workflows from n8n instance.
|
* Lists workflows from n8n instance.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import {
|
|||||||
UpdateNameOperation,
|
UpdateNameOperation,
|
||||||
AddTagOperation,
|
AddTagOperation,
|
||||||
RemoveTagOperation,
|
RemoveTagOperation,
|
||||||
|
ActivateWorkflowOperation,
|
||||||
|
DeactivateWorkflowOperation,
|
||||||
CleanStaleConnectionsOperation,
|
CleanStaleConnectionsOperation,
|
||||||
ReplaceConnectionsOperation
|
ReplaceConnectionsOperation
|
||||||
} from '../types/workflow-diff';
|
} from '../types/workflow-diff';
|
||||||
@@ -32,6 +34,7 @@ import { Workflow, WorkflowNode, WorkflowConnection } from '../types/n8n-api';
|
|||||||
import { Logger } from '../utils/logger';
|
import { Logger } from '../utils/logger';
|
||||||
import { validateWorkflowNode, validateWorkflowConnections } from './n8n-validation';
|
import { validateWorkflowNode, validateWorkflowConnections } from './n8n-validation';
|
||||||
import { sanitizeNode, sanitizeWorkflowNodes } from './node-sanitizer';
|
import { sanitizeNode, sanitizeWorkflowNodes } from './node-sanitizer';
|
||||||
|
import { isActivatableTrigger } from '../utils/node-type-utils';
|
||||||
|
|
||||||
const logger = new Logger({ prefix: '[WorkflowDiffEngine]' });
|
const logger = new Logger({ prefix: '[WorkflowDiffEngine]' });
|
||||||
|
|
||||||
@@ -214,12 +217,23 @@ export class WorkflowDiffEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const operationsApplied = request.operations.length;
|
const operationsApplied = request.operations.length;
|
||||||
|
|
||||||
|
// Extract activation flags from workflow object
|
||||||
|
const shouldActivate = (workflowCopy as any)._shouldActivate === true;
|
||||||
|
const shouldDeactivate = (workflowCopy as any)._shouldDeactivate === true;
|
||||||
|
|
||||||
|
// Clean up temporary flags
|
||||||
|
delete (workflowCopy as any)._shouldActivate;
|
||||||
|
delete (workflowCopy as any)._shouldDeactivate;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
workflow: workflowCopy,
|
workflow: workflowCopy,
|
||||||
operationsApplied,
|
operationsApplied,
|
||||||
message: `Successfully applied ${operationsApplied} operations (${nodeOperations.length} node ops, ${otherOperations.length} other ops)`,
|
message: `Successfully applied ${operationsApplied} operations (${nodeOperations.length} node ops, ${otherOperations.length} other ops)`,
|
||||||
warnings: this.warnings.length > 0 ? this.warnings : undefined
|
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
||||||
|
shouldActivate: shouldActivate || undefined,
|
||||||
|
shouldDeactivate: shouldDeactivate || undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -262,6 +276,10 @@ export class WorkflowDiffEngine {
|
|||||||
case 'addTag':
|
case 'addTag':
|
||||||
case 'removeTag':
|
case 'removeTag':
|
||||||
return null; // These are always valid
|
return null; // These are always valid
|
||||||
|
case 'activateWorkflow':
|
||||||
|
return this.validateActivateWorkflow(workflow, operation);
|
||||||
|
case 'deactivateWorkflow':
|
||||||
|
return this.validateDeactivateWorkflow(workflow, operation);
|
||||||
case 'cleanStaleConnections':
|
case 'cleanStaleConnections':
|
||||||
return this.validateCleanStaleConnections(workflow, operation);
|
return this.validateCleanStaleConnections(workflow, operation);
|
||||||
case 'replaceConnections':
|
case 'replaceConnections':
|
||||||
@@ -315,6 +333,12 @@ export class WorkflowDiffEngine {
|
|||||||
case 'removeTag':
|
case 'removeTag':
|
||||||
this.applyRemoveTag(workflow, operation);
|
this.applyRemoveTag(workflow, operation);
|
||||||
break;
|
break;
|
||||||
|
case 'activateWorkflow':
|
||||||
|
this.applyActivateWorkflow(workflow, operation);
|
||||||
|
break;
|
||||||
|
case 'deactivateWorkflow':
|
||||||
|
this.applyDeactivateWorkflow(workflow, operation);
|
||||||
|
break;
|
||||||
case 'cleanStaleConnections':
|
case 'cleanStaleConnections':
|
||||||
this.applyCleanStaleConnections(workflow, operation);
|
this.applyCleanStaleConnections(workflow, operation);
|
||||||
break;
|
break;
|
||||||
@@ -847,13 +871,46 @@ export class WorkflowDiffEngine {
|
|||||||
|
|
||||||
private applyRemoveTag(workflow: Workflow, operation: RemoveTagOperation): void {
|
private applyRemoveTag(workflow: Workflow, operation: RemoveTagOperation): void {
|
||||||
if (!workflow.tags) return;
|
if (!workflow.tags) return;
|
||||||
|
|
||||||
const index = workflow.tags.indexOf(operation.tag);
|
const index = workflow.tags.indexOf(operation.tag);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
workflow.tags.splice(index, 1);
|
workflow.tags.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workflow activation operation validators
|
||||||
|
private validateActivateWorkflow(workflow: Workflow, operation: ActivateWorkflowOperation): string | null {
|
||||||
|
// Check if workflow has at least one activatable trigger
|
||||||
|
// Issue #351: executeWorkflowTrigger cannot activate workflows
|
||||||
|
const activatableTriggers = workflow.nodes.filter(
|
||||||
|
node => !node.disabled && isActivatableTrigger(node.type)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (activatableTriggers.length === 0) {
|
||||||
|
return 'Cannot activate workflow: No activatable trigger nodes found. Workflows must have at least one enabled trigger node (webhook, schedule, email, etc.). Note: executeWorkflowTrigger cannot activate workflows as they can only be invoked by other workflows.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateDeactivateWorkflow(workflow: Workflow, operation: DeactivateWorkflowOperation): string | null {
|
||||||
|
// Deactivation is always valid - any workflow can be deactivated
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workflow activation operation appliers
|
||||||
|
private applyActivateWorkflow(workflow: Workflow, operation: ActivateWorkflowOperation): void {
|
||||||
|
// Set flag in workflow object to indicate activation intent
|
||||||
|
// The handler will call the API method after workflow update
|
||||||
|
(workflow as any)._shouldActivate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyDeactivateWorkflow(workflow: Workflow, operation: DeactivateWorkflowOperation): void {
|
||||||
|
// Set flag in workflow object to indicate deactivation intent
|
||||||
|
// The handler will call the API method after workflow update
|
||||||
|
(workflow as any)._shouldDeactivate = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Connection cleanup operation validators
|
// Connection cleanup operation validators
|
||||||
private validateCleanStaleConnections(workflow: Workflow, operation: CleanStaleConnectionsOperation): string | null {
|
private validateCleanStaleConnections(workflow: Workflow, operation: CleanStaleConnectionsOperation): string | null {
|
||||||
// This operation is always valid - it just cleans up what it finds
|
// This operation is always valid - it just cleans up what it finds
|
||||||
|
|||||||
@@ -114,6 +114,16 @@ export interface RemoveTagOperation extends DiffOperation {
|
|||||||
tag: string;
|
tag: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ActivateWorkflowOperation extends DiffOperation {
|
||||||
|
type: 'activateWorkflow';
|
||||||
|
// No additional properties needed - just activates the workflow
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeactivateWorkflowOperation extends DiffOperation {
|
||||||
|
type: 'deactivateWorkflow';
|
||||||
|
// No additional properties needed - just deactivates the workflow
|
||||||
|
}
|
||||||
|
|
||||||
// Connection Cleanup Operations
|
// Connection Cleanup Operations
|
||||||
export interface CleanStaleConnectionsOperation extends DiffOperation {
|
export interface CleanStaleConnectionsOperation extends DiffOperation {
|
||||||
type: 'cleanStaleConnections';
|
type: 'cleanStaleConnections';
|
||||||
@@ -148,6 +158,8 @@ export type WorkflowDiffOperation =
|
|||||||
| UpdateNameOperation
|
| UpdateNameOperation
|
||||||
| AddTagOperation
|
| AddTagOperation
|
||||||
| RemoveTagOperation
|
| RemoveTagOperation
|
||||||
|
| ActivateWorkflowOperation
|
||||||
|
| DeactivateWorkflowOperation
|
||||||
| CleanStaleConnectionsOperation
|
| CleanStaleConnectionsOperation
|
||||||
| ReplaceConnectionsOperation;
|
| ReplaceConnectionsOperation;
|
||||||
|
|
||||||
@@ -176,6 +188,8 @@ export interface WorkflowDiffResult {
|
|||||||
applied?: number[]; // Indices of successfully applied operations (when continueOnError is true)
|
applied?: number[]; // Indices of successfully applied operations (when continueOnError is true)
|
||||||
failed?: number[]; // Indices of failed operations (when continueOnError is true)
|
failed?: number[]; // Indices of failed operations (when continueOnError is true)
|
||||||
staleConnectionsRemoved?: Array<{ from: string; to: string }>; // For cleanStaleConnections operation
|
staleConnectionsRemoved?: Array<{ from: string; to: string }>; // For cleanStaleConnections operation
|
||||||
|
shouldActivate?: boolean; // Flag to activate workflow after update (for activateWorkflow operation)
|
||||||
|
shouldDeactivate?: boolean; // Flag to deactivate workflow after update (for deactivateWorkflow operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper type for node reference (supports both ID and name)
|
// Helper type for node reference (supports both ID and name)
|
||||||
|
|||||||
Reference in New Issue
Block a user