refactor: clean up file names and fix version management

- Renamed files to remove unnecessary suffixes:
  - tools-update.ts → tools.ts
  - server-update.ts → server.ts
  - http-server-fixed.ts → http-server.ts
- Created version utility to read from package.json as single source of truth
- Updated all imports across 21+ files
- Removed legacy files:
  - src/http-server.ts (legacy HTTP server with known issues)
  - src/utils/n8n-client.ts (unused legacy API client)
- Added n8n_diagnostic tool to help troubleshoot management tools visibility
- Added script to sync package.runtime.json version
- Fixed version mismatch issue (was hardcoded 2.4.1, now reads 2.7.0 from package.json)

This addresses GitHub issue #5 regarding version mismatch and provides better diagnostics for users.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-06-29 17:43:29 +02:00
parent a1e3f9cb39
commit 91386b2d02
23 changed files with 369 additions and 631 deletions

View File

@@ -1,168 +0,0 @@
import { N8NConfig } from '../types';
export class N8NApiClient {
private config: N8NConfig;
private headers: Record<string, string>;
constructor(config: N8NConfig) {
this.config = config;
this.headers = {
'Content-Type': 'application/json',
'X-N8N-API-KEY': config.apiKey,
};
}
private async request(endpoint: string, options: RequestInit = {}): Promise<any> {
const url = `${this.config.apiUrl}/api/v1${endpoint}`;
try {
const response = await fetch(url, {
...options,
headers: {
...this.headers,
...options.headers,
},
});
if (!response.ok) {
const error = await response.text();
throw new Error(`n8n API error: ${response.status} - ${error}`);
}
return await response.json();
} catch (error) {
throw new Error(`Failed to connect to n8n: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
// Workflow operations
async getWorkflows(filters?: { active?: boolean; tags?: string[] }): Promise<any> {
const query = new URLSearchParams();
if (filters?.active !== undefined) {
query.append('active', filters.active.toString());
}
if (filters?.tags?.length) {
query.append('tags', filters.tags.join(','));
}
return this.request(`/workflows${query.toString() ? `?${query}` : ''}`);
}
async getWorkflow(id: string): Promise<any> {
return this.request(`/workflows/${id}`);
}
async createWorkflow(workflowData: any): Promise<any> {
return this.request('/workflows', {
method: 'POST',
body: JSON.stringify(workflowData),
});
}
async updateWorkflow(id: string, updates: any): Promise<any> {
return this.request(`/workflows/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates),
});
}
async deleteWorkflow(id: string): Promise<any> {
return this.request(`/workflows/${id}`, {
method: 'DELETE',
});
}
async activateWorkflow(id: string): Promise<any> {
return this.request(`/workflows/${id}/activate`, {
method: 'POST',
});
}
async deactivateWorkflow(id: string): Promise<any> {
return this.request(`/workflows/${id}/deactivate`, {
method: 'POST',
});
}
// Execution operations
async executeWorkflow(id: string, data?: any): Promise<any> {
return this.request(`/workflows/${id}/execute`, {
method: 'POST',
body: JSON.stringify({ data }),
});
}
async getExecutions(filters?: {
workflowId?: string;
status?: string;
limit?: number
}): Promise<any> {
const query = new URLSearchParams();
if (filters?.workflowId) {
query.append('workflowId', filters.workflowId);
}
if (filters?.status) {
query.append('status', filters.status);
}
if (filters?.limit) {
query.append('limit', filters.limit.toString());
}
return this.request(`/executions${query.toString() ? `?${query}` : ''}`);
}
async getExecution(id: string): Promise<any> {
return this.request(`/executions/${id}`);
}
async deleteExecution(id: string): Promise<any> {
return this.request(`/executions/${id}`, {
method: 'DELETE',
});
}
// Credential operations
async getCredentialTypes(): Promise<any> {
return this.request('/credential-types');
}
async getCredentials(): Promise<any> {
return this.request('/credentials');
}
// Node operations
async getNodeTypes(): Promise<any> {
return this.request('/node-types');
}
async getNodeType(nodeType: string): Promise<any> {
return this.request(`/node-types/${nodeType}`);
}
// Extended methods for node source extraction
async getNodeSourceCode(nodeType: string): Promise<any> {
// This is a special endpoint we'll need to handle differently
// as n8n doesn't expose source code directly through API
// We'll need to implement this through file system access
throw new Error('Node source code extraction requires special implementation');
}
async getNodeDescription(nodeType: string): Promise<any> {
try {
const nodeTypeData = await this.getNodeType(nodeType);
return {
name: nodeTypeData.name,
displayName: nodeTypeData.displayName,
description: nodeTypeData.description,
version: nodeTypeData.version,
defaults: nodeTypeData.defaults,
inputs: nodeTypeData.inputs,
outputs: nodeTypeData.outputs,
properties: nodeTypeData.properties,
credentials: nodeTypeData.credentials,
};
} catch (error) {
throw new Error(`Failed to get node description: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
}

19
src/utils/version.ts Normal file
View File

@@ -0,0 +1,19 @@
import { readFileSync } from 'fs';
import { join } from 'path';
/**
* Get the project version from package.json
* This ensures we have a single source of truth for versioning
*/
function getProjectVersion(): string {
try {
const packageJsonPath = join(__dirname, '../../package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
return packageJson.version || '0.0.0';
} catch (error) {
console.error('Failed to read version from package.json:', error);
return '0.0.0';
}
}
export const PROJECT_VERSION = getProjectVersion();