@codesys/mcp-toolkit 1.4.8 → 1.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.
- package/CHANGELOG.md +22 -0
- package/dist/codesys_interop.js +2 -2
- package/dist/direct_session_command.js +261 -0
- package/dist/scripts/start_session.py +104 -7
- package/dist/server.js +31 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.5.1] - 2025-05-02
|
|
9
|
+
### Added
|
|
10
|
+
- Implemented direct session command handling for project creation
|
|
11
|
+
- Added ability to communicate directly with active sessions via command.json
|
|
12
|
+
- Created a simplified project creation script optimized for sessions
|
|
13
|
+
- Added fallback to standard execution if direct approach fails
|
|
14
|
+
|
|
15
|
+
## [1.5.0] - 2025-05-02
|
|
16
|
+
### Changed
|
|
17
|
+
- Temporarily disabled session management for create_project operations to ensure reliability
|
|
18
|
+
- Reverted to direct script execution model for project creation operations
|
|
19
|
+
- Project creation now uses a simpler, more reliable approach that works consistently
|
|
20
|
+
|
|
21
|
+
## [1.4.9] - 2025-05-02
|
|
22
|
+
### Fixed
|
|
23
|
+
- Fixed critical issue with create_project not succeeding in persistent sessions
|
|
24
|
+
- Added comprehensive debugging for project creation operations
|
|
25
|
+
- Improved result file handling to prevent file access issues
|
|
26
|
+
- Enhanced success detection for project file creation when opening fails
|
|
27
|
+
- Fixed issue with result files not being properly written during critical operations
|
|
28
|
+
- Added atomic file writing with fallback mechanisms for result files
|
|
29
|
+
|
|
8
30
|
## [1.4.8] - 2025-05-02
|
|
9
31
|
### Fixed
|
|
10
32
|
- Fixed critical bug with project creation in persistent sessions
|
package/dist/codesys_interop.js
CHANGED
|
@@ -91,8 +91,8 @@ function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName,
|
|
|
91
91
|
if (!fs.existsSync(codesysExePath))
|
|
92
92
|
throw new Error(`CODESYS executable not found at provided path: ${codesysExePath}`);
|
|
93
93
|
// --- End Pre-checks ---
|
|
94
|
-
//
|
|
95
|
-
const SESSION_FEATURE_DISABLED =
|
|
94
|
+
// Session management feature control - TEMPORARILY DISABLE FOR DEBUGGING
|
|
95
|
+
const SESSION_FEATURE_DISABLED = true; // Session feature disabled for now
|
|
96
96
|
// --- Check if we should use the persistent session ---
|
|
97
97
|
if (!SESSION_FEATURE_DISABLED && sessionManager) {
|
|
98
98
|
const sessionState = sessionManager.getState();
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Direct Session Command Tool
|
|
4
|
+
*
|
|
5
|
+
* This file provides functions to directly interact with a CODESYS session
|
|
6
|
+
* by writing to the command.json file and reading from the result.json file.
|
|
7
|
+
*
|
|
8
|
+
* This is an alternative approach to the standard session management.
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
44
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
45
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
46
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
47
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
48
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
49
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.isSessionActive = isSessionActive;
|
|
54
|
+
exports.executeScriptDirect = executeScriptDirect;
|
|
55
|
+
exports.createProjectDirect = createProjectDirect;
|
|
56
|
+
const fs = __importStar(require("fs"));
|
|
57
|
+
const path = __importStar(require("path"));
|
|
58
|
+
const os = __importStar(require("os"));
|
|
59
|
+
const uuid_1 = require("uuid");
|
|
60
|
+
// Session directory and file names
|
|
61
|
+
const SESSION_DIR = path.join(os.tmpdir(), 'codesys_session');
|
|
62
|
+
const COMMAND_FILE = path.join(SESSION_DIR, 'command.json');
|
|
63
|
+
const RESULT_FILE = path.join(SESSION_DIR, 'result.json');
|
|
64
|
+
const SESSION_LOCK_FILE = path.join(SESSION_DIR, 'session.lock');
|
|
65
|
+
const STATE_FILE = path.join(SESSION_DIR, 'session_state.json');
|
|
66
|
+
/**
|
|
67
|
+
* Check if a session is active by looking for the lock file
|
|
68
|
+
* @returns True if a session appears to be active
|
|
69
|
+
*/
|
|
70
|
+
function isSessionActive() {
|
|
71
|
+
try {
|
|
72
|
+
// Check if session directory exists
|
|
73
|
+
if (!fs.existsSync(SESSION_DIR)) {
|
|
74
|
+
console.error(`SESSION_DIR doesn't exist: ${SESSION_DIR}`);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// Check if lock file exists
|
|
78
|
+
if (!fs.existsSync(SESSION_LOCK_FILE)) {
|
|
79
|
+
console.error(`SESSION_LOCK_FILE doesn't exist: ${SESSION_LOCK_FILE}`);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
// Check if command file exists
|
|
83
|
+
if (!fs.existsSync(COMMAND_FILE)) {
|
|
84
|
+
console.error(`COMMAND_FILE doesn't exist: ${COMMAND_FILE}`);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
// Read the lock file to check age
|
|
88
|
+
const lockFileContent = fs.readFileSync(SESSION_LOCK_FILE, 'utf8');
|
|
89
|
+
const lockTime = parseFloat(lockFileContent);
|
|
90
|
+
const currentTime = Date.now() / 1000; // convert to seconds
|
|
91
|
+
const lockAgeSeconds = currentTime - lockTime;
|
|
92
|
+
// If lock is too old, session might be dead
|
|
93
|
+
if (lockAgeSeconds > 30) {
|
|
94
|
+
console.error(`Session lock file is too old (${lockAgeSeconds.toFixed(1)} seconds)`);
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error(`Error checking session: ${error}`);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Execute a script by directly writing to the command.json file
|
|
106
|
+
* @param scriptContent The Python script to execute
|
|
107
|
+
* @param timeoutMs Timeout in milliseconds
|
|
108
|
+
* @returns Promise that resolves to the script result
|
|
109
|
+
*/
|
|
110
|
+
function executeScriptDirect(scriptContent_1) {
|
|
111
|
+
return __awaiter(this, arguments, void 0, function* (scriptContent, timeoutMs = 60000) {
|
|
112
|
+
if (!isSessionActive()) {
|
|
113
|
+
throw new Error('No active session found');
|
|
114
|
+
}
|
|
115
|
+
// Generate a unique ID for this execution
|
|
116
|
+
const scriptId = (0, uuid_1.v4)();
|
|
117
|
+
// Create command data
|
|
118
|
+
const commandData = {
|
|
119
|
+
status: "pending",
|
|
120
|
+
command: {
|
|
121
|
+
id: scriptId,
|
|
122
|
+
type: "execute_script",
|
|
123
|
+
script: scriptContent,
|
|
124
|
+
timestamp: Date.now()
|
|
125
|
+
},
|
|
126
|
+
timestamp: Date.now()
|
|
127
|
+
};
|
|
128
|
+
console.error(`DIRECT_SESSION: Writing command to ${COMMAND_FILE}`);
|
|
129
|
+
// Write command to file
|
|
130
|
+
fs.writeFileSync(COMMAND_FILE, JSON.stringify(commandData, null, 2));
|
|
131
|
+
console.error(`DIRECT_SESSION: Command written to file`);
|
|
132
|
+
// Poll for result
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
let pollCount = 0;
|
|
135
|
+
const pollInterval = setInterval(() => {
|
|
136
|
+
pollCount++;
|
|
137
|
+
try {
|
|
138
|
+
// Check if result file exists
|
|
139
|
+
if (!fs.existsSync(RESULT_FILE)) {
|
|
140
|
+
if (pollCount % 10 === 0) {
|
|
141
|
+
console.error(`DIRECT_SESSION: Result file not found at ${RESULT_FILE} (poll ${pollCount})`);
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Read result file
|
|
146
|
+
const resultContent = fs.readFileSync(RESULT_FILE, 'utf8');
|
|
147
|
+
try {
|
|
148
|
+
const resultData = JSON.parse(resultContent);
|
|
149
|
+
// Check if the result is complete and for our script
|
|
150
|
+
if (resultData.status === 'complete' && resultData.result) {
|
|
151
|
+
const result = resultData.result;
|
|
152
|
+
if (result.id === scriptId) {
|
|
153
|
+
console.error(`DIRECT_SESSION: Found result for script ${scriptId}`);
|
|
154
|
+
// Clear interval
|
|
155
|
+
clearInterval(pollInterval);
|
|
156
|
+
// Resolve with the result
|
|
157
|
+
resolve({
|
|
158
|
+
success: result.success,
|
|
159
|
+
output: result.output
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (parseError) {
|
|
166
|
+
console.error(`DIRECT_SESSION: Error parsing result data: ${parseError}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
console.error(`DIRECT_SESSION: Error reading result file: ${error}`);
|
|
171
|
+
}
|
|
172
|
+
}, 100);
|
|
173
|
+
// Set timeout
|
|
174
|
+
setTimeout(() => {
|
|
175
|
+
clearInterval(pollInterval);
|
|
176
|
+
reject(new Error(`Script execution timed out after ${timeoutMs}ms`));
|
|
177
|
+
}, timeoutMs);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Create a project using a session
|
|
183
|
+
* @param targetProjectPath Full path to the project to create
|
|
184
|
+
* @param templateProjectPath Path to the template project
|
|
185
|
+
* @returns Promise that resolves when the project is created
|
|
186
|
+
*/
|
|
187
|
+
function createProjectDirect(targetProjectPath, templateProjectPath) {
|
|
188
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
189
|
+
try {
|
|
190
|
+
if (!isSessionActive()) {
|
|
191
|
+
throw new Error('No active session found');
|
|
192
|
+
}
|
|
193
|
+
// Normalize paths for cross-platform compatibility
|
|
194
|
+
const normalizedTargetPath = targetProjectPath.replace(/\\/g, '/');
|
|
195
|
+
const normalizedTemplatePath = templateProjectPath.replace(/\\/g, '/');
|
|
196
|
+
// Create a simplified project creation script that only copies the file and doesn't try to open it
|
|
197
|
+
const simpleProjectCreationScript = `
|
|
198
|
+
import sys, os, shutil, time, traceback
|
|
199
|
+
|
|
200
|
+
TEMPLATE_PROJECT_PATH = '${normalizedTemplatePath}'
|
|
201
|
+
PROJECT_FILE_PATH = '${normalizedTargetPath}'
|
|
202
|
+
|
|
203
|
+
print("Direct project creation script")
|
|
204
|
+
print(" Template: %s" % TEMPLATE_PROJECT_PATH)
|
|
205
|
+
print(" Target: %s" % PROJECT_FILE_PATH)
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
# Ensure the target directory exists
|
|
209
|
+
target_dir = os.path.dirname(PROJECT_FILE_PATH)
|
|
210
|
+
if not os.path.exists(target_dir):
|
|
211
|
+
print("Creating target directory: %s" % target_dir)
|
|
212
|
+
os.makedirs(target_dir)
|
|
213
|
+
|
|
214
|
+
# Check if template exists
|
|
215
|
+
if not os.path.exists(TEMPLATE_PROJECT_PATH):
|
|
216
|
+
print("ERROR: Template project not found: %s" % TEMPLATE_PROJECT_PATH)
|
|
217
|
+
print("SCRIPT_ERROR: Template project not found")
|
|
218
|
+
sys.exit(1)
|
|
219
|
+
|
|
220
|
+
# Copy the template to the target
|
|
221
|
+
print("Copying template to target...")
|
|
222
|
+
shutil.copy2(TEMPLATE_PROJECT_PATH, PROJECT_FILE_PATH)
|
|
223
|
+
|
|
224
|
+
# Verify the file was created
|
|
225
|
+
if os.path.exists(PROJECT_FILE_PATH):
|
|
226
|
+
print("Project file created successfully: %s" % PROJECT_FILE_PATH)
|
|
227
|
+
print("File size: %d bytes" % os.path.getsize(PROJECT_FILE_PATH))
|
|
228
|
+
print("SCRIPT_SUCCESS: Project file created successfully")
|
|
229
|
+
else:
|
|
230
|
+
print("ERROR: Failed to create project file")
|
|
231
|
+
print("SCRIPT_ERROR: Failed to create project file")
|
|
232
|
+
sys.exit(1)
|
|
233
|
+
except Exception as e:
|
|
234
|
+
print("ERROR: %s" % e)
|
|
235
|
+
traceback.print_exc()
|
|
236
|
+
print("SCRIPT_ERROR: %s" % e)
|
|
237
|
+
sys.exit(1)
|
|
238
|
+
`;
|
|
239
|
+
// Execute the script directly
|
|
240
|
+
const result = yield executeScriptDirect(simpleProjectCreationScript, 120000);
|
|
241
|
+
if (result.success) {
|
|
242
|
+
return {
|
|
243
|
+
success: true,
|
|
244
|
+
message: `Project created successfully at ${targetProjectPath}`
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
return {
|
|
249
|
+
success: false,
|
|
250
|
+
message: `Failed to create project: ${result.output}`
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
return {
|
|
256
|
+
success: false,
|
|
257
|
+
message: `Error: ${error.message}`
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
@@ -287,8 +287,28 @@ def execute_command(command_data):
|
|
|
287
287
|
# For project creation/opening, we'll execute the script with some guards
|
|
288
288
|
print("Executing critical script with guards...")
|
|
289
289
|
try:
|
|
290
|
-
# Execute the script
|
|
290
|
+
# Execute the script in a more controlled manner with debugging
|
|
291
|
+
print("DEBUG-CRITICAL: About to execute project creation/open script")
|
|
292
|
+
# First write project ops directly to the result file to prevent hanging
|
|
293
|
+
try:
|
|
294
|
+
with open(RESULT_FILE, 'w') as f:
|
|
295
|
+
result_data = {
|
|
296
|
+
"status": "in_progress",
|
|
297
|
+
"result": {
|
|
298
|
+
"id": command.get("id", "unknown"),
|
|
299
|
+
"success": False,
|
|
300
|
+
"output": "Project creation/opening operation in progress...",
|
|
301
|
+
},
|
|
302
|
+
"timestamp": time.time()
|
|
303
|
+
}
|
|
304
|
+
json.dump(result_data, f, indent=2)
|
|
305
|
+
print("DEBUG-CRITICAL: Created preliminary result file")
|
|
306
|
+
except Exception as rf_err:
|
|
307
|
+
print("DEBUG-CRITICAL: Error creating preliminary result: {0}".format(rf_err))
|
|
308
|
+
|
|
309
|
+
# Now execute the script
|
|
291
310
|
exec(script, local_globals)
|
|
311
|
+
print("DEBUG-CRITICAL: Script executed successfully")
|
|
292
312
|
except Exception as e:
|
|
293
313
|
# If the script fails, capture the error
|
|
294
314
|
print("ERROR in critical script execution: {0}".format(e))
|
|
@@ -315,6 +335,32 @@ def execute_command(command_data):
|
|
|
315
335
|
stdout_output = stdout_buffer.getvalue()
|
|
316
336
|
stderr_output = stderr_buffer.getvalue()
|
|
317
337
|
|
|
338
|
+
# Special handling for project creation - always check if we can find success markers
|
|
339
|
+
# in partial output even if script appears to have issues
|
|
340
|
+
if has_project_create:
|
|
341
|
+
# Look specifically for the file copy completion
|
|
342
|
+
if "File copy VERIFIED - file exists at destination" in stdout_output or "DEBUG: File copy complete" in stdout_output:
|
|
343
|
+
print("IMPORTANT: Detected successful file copy in project creation script")
|
|
344
|
+
project_path = ""
|
|
345
|
+
|
|
346
|
+
# Try to extract the project path from output
|
|
347
|
+
project_path_match = None
|
|
348
|
+
for line in stdout_output.split('\n'):
|
|
349
|
+
if "DEBUG: Target Path =" in line:
|
|
350
|
+
project_path_match = line.split('=')[1].strip()
|
|
351
|
+
break
|
|
352
|
+
|
|
353
|
+
if project_path_match:
|
|
354
|
+
project_path = project_path_match
|
|
355
|
+
print("IMPORTANT: Found project path: {0}".format(project_path))
|
|
356
|
+
|
|
357
|
+
# Even if there was an error later (like opening), consider this a partial success
|
|
358
|
+
if "Error creating project" in stdout_output and "currently in use" in stdout_output:
|
|
359
|
+
print("IMPORTANT: Project file was created but couldn't be opened due to 'in use' error")
|
|
360
|
+
# Combine success and error markers
|
|
361
|
+
stdout_output += "\nSCRIPT_SUCCESS: Project file was created at {0} but could not be opened because it is in use by another CODESYS instance.".format(project_path)
|
|
362
|
+
result["success"] = True
|
|
363
|
+
|
|
318
364
|
# Check for success/error markers
|
|
319
365
|
if SCRIPT_SUCCESS_MARKER in stdout_output or SCRIPT_SUCCESS_MARKER in stderr_output:
|
|
320
366
|
result["success"] = True
|
|
@@ -323,9 +369,18 @@ def execute_command(command_data):
|
|
|
323
369
|
result["success"] = False
|
|
324
370
|
print("Found error marker in script output")
|
|
325
371
|
else:
|
|
326
|
-
# No explicit markers, assume success
|
|
327
|
-
|
|
328
|
-
|
|
372
|
+
# No explicit markers, assume success for non-critical operations
|
|
373
|
+
# For critical operations like project creation, be more careful
|
|
374
|
+
if has_project_create:
|
|
375
|
+
if "File copy complete" in stdout_output:
|
|
376
|
+
result["success"] = True
|
|
377
|
+
print("Project creation seems successful based on file copy completion")
|
|
378
|
+
else:
|
|
379
|
+
result["success"] = False
|
|
380
|
+
print("Project creation script lacks success markers and file copy confirmation")
|
|
381
|
+
else:
|
|
382
|
+
result["success"] = True
|
|
383
|
+
print("No markers found in script output, assuming success")
|
|
329
384
|
|
|
330
385
|
# Tally output length to check if output is reasonable
|
|
331
386
|
print("Output length: stdout={0} chars, stderr={1} chars".format(
|
|
@@ -450,7 +505,7 @@ def check_command():
|
|
|
450
505
|
result, stop_requested = execute_command(command)
|
|
451
506
|
print("Command execution complete, stop_requested: {0}".format(stop_requested))
|
|
452
507
|
|
|
453
|
-
# Write the result
|
|
508
|
+
# Write the result with extra safety measures
|
|
454
509
|
result_data = {
|
|
455
510
|
"status": "complete",
|
|
456
511
|
"result": result,
|
|
@@ -458,8 +513,50 @@ def check_command():
|
|
|
458
513
|
}
|
|
459
514
|
|
|
460
515
|
print("Writing result to {0}".format(RESULT_FILE))
|
|
461
|
-
|
|
462
|
-
|
|
516
|
+
sys.stdout.flush()
|
|
517
|
+
|
|
518
|
+
# First, write to a temporary file to avoid corruption
|
|
519
|
+
temp_result_file = RESULT_FILE + ".tmp"
|
|
520
|
+
try:
|
|
521
|
+
with open(temp_result_file, 'w') as f:
|
|
522
|
+
json.dump(result_data, f, indent=2)
|
|
523
|
+
print("Temporary result file written successfully")
|
|
524
|
+
|
|
525
|
+
# If temporary write succeeded, move to the real file
|
|
526
|
+
if os.path.exists(temp_result_file):
|
|
527
|
+
# On Windows, we may need to remove the destination first
|
|
528
|
+
if os.path.exists(RESULT_FILE):
|
|
529
|
+
try:
|
|
530
|
+
os.remove(RESULT_FILE)
|
|
531
|
+
except Exception as rm_err:
|
|
532
|
+
print("Warning: Could not remove existing result file: {0}".format(rm_err))
|
|
533
|
+
|
|
534
|
+
# Now rename the temp file to the real file
|
|
535
|
+
os.rename(temp_result_file, RESULT_FILE)
|
|
536
|
+
print("Result file updated successfully")
|
|
537
|
+
else:
|
|
538
|
+
print("ERROR: Temporary result file not found after writing!")
|
|
539
|
+
# Try direct write as fallback
|
|
540
|
+
with open(RESULT_FILE, 'w') as f:
|
|
541
|
+
json.dump(result_data, f, indent=2)
|
|
542
|
+
except Exception as result_err:
|
|
543
|
+
print("ERROR writing result file: {0}".format(result_err))
|
|
544
|
+
traceback.print_exc()
|
|
545
|
+
sys.stdout.flush()
|
|
546
|
+
|
|
547
|
+
# Desperate attempt - try direct write with minimal content
|
|
548
|
+
try:
|
|
549
|
+
with open(RESULT_FILE, 'w') as f:
|
|
550
|
+
f.write('{"status":"complete","result":{"id":"' +
|
|
551
|
+
result["id"] + '","success":' +
|
|
552
|
+
('true' if result["success"] else 'false') +
|
|
553
|
+
',"output":"Result writing failed but operation completed"},"timestamp":' +
|
|
554
|
+
str(time.time()) + '}')
|
|
555
|
+
print("Emergency minimal result file written")
|
|
556
|
+
except Exception as emergency_err:
|
|
557
|
+
print("CRITICAL ERROR: Even emergency result write failed: {0}".format(emergency_err))
|
|
558
|
+
|
|
559
|
+
sys.stdout.flush()
|
|
463
560
|
|
|
464
561
|
# Reset command file
|
|
465
562
|
command_data = {
|
package/dist/server.js
CHANGED
|
@@ -69,6 +69,7 @@ const promises_1 = require("fs/promises");
|
|
|
69
69
|
const fs = __importStar(require("fs"));
|
|
70
70
|
const scriptLoader_1 = require("./scriptLoader");
|
|
71
71
|
const sessionManager_1 = require("./sessionManager");
|
|
72
|
+
const direct_session_command_1 = require("./direct_session_command");
|
|
72
73
|
// --- Wrap server logic in an exported function ---
|
|
73
74
|
function startMcpServer(config) {
|
|
74
75
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -387,30 +388,42 @@ function startMcpServer(config) {
|
|
|
387
388
|
'SKIP_OPEN': inSession ? 'true' : 'false'
|
|
388
389
|
});
|
|
389
390
|
console.error(">>> create_project (copy-then-open): PREPARED SCRIPT:", scriptContent.substring(0, 500) + "...");
|
|
390
|
-
//
|
|
391
|
-
// This helps prevent timing out on slower systems or when CODESYS is busy
|
|
391
|
+
// Try the direct session approach first if a session is active
|
|
392
392
|
let result;
|
|
393
|
-
|
|
394
|
-
if
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
console.error(">>> create_project: Using session with extended timeout (120s)");
|
|
393
|
+
let usingDirectSession = false;
|
|
394
|
+
// Check if there's an active session using our direct method
|
|
395
|
+
if ((0, direct_session_command_1.isSessionActive)()) {
|
|
396
|
+
console.error(">>> create_project: Detected active session, using direct command approach");
|
|
398
397
|
try {
|
|
399
|
-
//
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
398
|
+
// Use our direct session command approach
|
|
399
|
+
usingDirectSession = true;
|
|
400
|
+
const directResult = yield (0, direct_session_command_1.createProjectDirect)(normalizedTargetPath, normalizedTemplatePath);
|
|
401
|
+
if (directResult.success) {
|
|
402
|
+
// Format the result to match the expected format
|
|
403
|
+
result = {
|
|
404
|
+
success: true,
|
|
405
|
+
output: `SCRIPT_SUCCESS: Project created from template using direct session command.\n${directResult.message}`
|
|
406
|
+
};
|
|
407
|
+
console.error(">>> create_project: Direct session command succeeded");
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
console.error(`>>> create_project: Direct session command failed: ${directResult.message}`);
|
|
411
|
+
// Fall back to regular script execution
|
|
412
|
+
usingDirectSession = false;
|
|
413
|
+
result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName);
|
|
414
|
+
}
|
|
405
415
|
}
|
|
406
|
-
catch (
|
|
407
|
-
console.error(`>>> create_project:
|
|
408
|
-
// Fall back to
|
|
416
|
+
catch (directError) {
|
|
417
|
+
console.error(`>>> create_project: Error with direct session command: ${directError.message}`);
|
|
418
|
+
// Fall back to regular script execution
|
|
419
|
+
usingDirectSession = false;
|
|
409
420
|
result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName);
|
|
410
421
|
}
|
|
411
422
|
}
|
|
412
|
-
|
|
413
|
-
|
|
423
|
+
// If not using direct session, use the standard approach
|
|
424
|
+
if (!usingDirectSession) {
|
|
425
|
+
// Use a longer timeout for project creation operations (120 seconds)
|
|
426
|
+
console.error(">>> create_project: Using standard script execution");
|
|
414
427
|
result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
|
|
415
428
|
}
|
|
416
429
|
console.error(">>> create_project (copy-then-open): EXECUTION RESULT:", JSON.stringify(result));
|