codesysultra 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ /**
3
+ * CODESYS Interop Module
4
+ * Handles direct interaction with the CODESYS executable via command-line scripts.
5
+ *
6
+ * This module manages:
7
+ * - Creating temporary Python script files
8
+ * - Executing them via CODESYS's scripting engine
9
+ * - Capturing and processing results
10
+ *
11
+ * IMPORTANT: Path handling for Windows is critical - paths with spaces require
12
+ * special handling to avoid the 'C:\Program' not recognized error.
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
48
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
49
+ return new (P || (P = Promise))(function (resolve, reject) {
50
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
51
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
52
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
53
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
54
+ });
55
+ };
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ exports.executeCodesysScript = executeCodesysScript;
58
+ const child_process_1 = require("child_process");
59
+ const promises_1 = require("fs/promises");
60
+ const path = __importStar(require("path"));
61
+ const os = __importStar(require("os"));
62
+ const fs = __importStar(require("fs")); // Import fs for existsSync check
63
+ // Define expected success/error markers from the Python scripts
64
+ const SCRIPT_SUCCESS_MARKER = 'SCRIPT_SUCCESS';
65
+ const SCRIPT_ERROR_MARKER = 'SCRIPT_ERROR';
66
+ /**
67
+ * Executes a CODESYS Python script using the command line interface.
68
+ *
69
+ * @param scriptContent The Python script code to execute.
70
+ * @param codesysExePath The full path to the CODESYS.exe executable (can contain spaces).
71
+ * @param codesysProfileName The name of the CODESYS profile to use for scripting.
72
+ * @returns A promise resolving to an object containing the success status and the script's output.
73
+ *
74
+ * NOTE: Using shell: true with careful command string quoting to handle
75
+ * CODESYS argument parsing quirks when launched non-interactively.
76
+ */
77
+ function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ // --- Pre-checks ---
80
+ if (!codesysExePath)
81
+ throw new Error('CODESYS executable path was not provided.');
82
+ if (!codesysProfileName)
83
+ throw new Error('CODESYS profile name was not provided.');
84
+ if (!fs.existsSync(codesysExePath))
85
+ throw new Error(`CODESYS executable not found at provided path: ${codesysExePath}`);
86
+ // --- End Pre-checks ---
87
+ const tempDir = os.tmpdir();
88
+ const tempFileName = `codesys_script_${Date.now()}_${Math.random().toString(36).substring(2, 9)}.py`;
89
+ const tempFilePath = path.join(tempDir, tempFileName); // Path module uses OS-specific separators ('\' on Windows)
90
+ let output = '';
91
+ let stderrOutput = '';
92
+ let success = false;
93
+ let exitCode = null;
94
+ const codesysDir = path.dirname(codesysExePath); // Directory containing CODESYS.exe
95
+ try {
96
+ const normalizedScriptContent = scriptContent.replace(/\r\n/g, '\n'); // Normalize line endings
97
+ // <<< --- ADDED SCRIPT CONTENT LOGGING --- >>>
98
+ process.stderr.write(`INTEROP: Script content to be written (first 500 chars):\n`);
99
+ process.stderr.write(`------ START SCRIPT (TEMP FILE) -----\n`);
100
+ process.stderr.write(`${normalizedScriptContent.substring(0, 500)}\n`); // Log the first 500 chars
101
+ process.stderr.write(`------ END SCRIPT SNIPPET (TEMP FILE) -----\n`);
102
+ // <<< --- END SCRIPT CONTENT LOGGING --- >>>
103
+ yield (0, promises_1.writeFile)(tempFilePath, normalizedScriptContent, 'latin1'); // Write the normalized content
104
+ process.stderr.write(`INTEROP: Temp script written: ${tempFilePath}\n`);
105
+ // --- Construct command string for shell: true ---
106
+ // Quote the executable path itself
107
+ const quotedExePath = `"${codesysExePath}"`;
108
+ // Format arguments exactly as CODESYS seems to want: --option="Value With Spaces"
109
+ // The outer quotes are for the shell parser.
110
+ const profileArg = `--profile="${codesysProfileName}"`;
111
+ const scriptArg = `--runscript="${tempFilePath}"`; // tempFilePath from path.join has correct backslashes for Win
112
+ // Combine into a single string for the shell
113
+ const fullCommandString = `${quotedExePath} ${profileArg} --noUI ${scriptArg}`;
114
+ // Example result: "\"C:\\Program Files\\...\\CODESYS.exe\" --profile=\"CODESYS V3.5 SP21\" --noUI --runscript=\"C:\\Users\\...\\script.py\""
115
+ // --- End command string construction ---
116
+ process.stderr.write(`INTEROP: Spawning command (shell:true): ${fullCommandString}\n`);
117
+ process.stderr.write(`INTEROP ENV: CWD before spawn: ${process.cwd()}\n`);
118
+ process.stderr.write(`INTEROP ENV: Forcing CWD for spawn: ${codesysDir}\n`); // Re-enabled CWD change
119
+ // --- Create modified environment (Re-enabled) ---
120
+ const spawnEnv = Object.assign({}, process.env);
121
+ const pathSeparator = ';'; // Windows
122
+ const originalPath = spawnEnv.PATH || '';
123
+ spawnEnv.PATH = `${codesysDir}${pathSeparator}${originalPath}`; // Prepend CODESYS dir to PATH
124
+ process.stderr.write(`INTEROP ENV: MODIFIED PATH for spawn (prepended): ${spawnEnv.PATH.substring(0, 100)}...\n`); // Re-enabled ENV change
125
+ // --- End modified environment ---
126
+ const spawnResult = yield new Promise((resolve) => {
127
+ let stdoutData = '';
128
+ let stderrData = '';
129
+ const controller = new AbortController();
130
+ const timeoutSignal = controller.signal;
131
+ const timeoutDuration = 60000; // 60 seconds
132
+ // Pass the single command string, empty args array, and shell: true
133
+ const childProcess = (0, child_process_1.spawn)(fullCommandString, [], {
134
+ windowsHide: true,
135
+ signal: timeoutSignal,
136
+ cwd: codesysDir, // Re-enabled CWD change
137
+ env: spawnEnv, // Re-enabled ENV change
138
+ shell: true // USE shell: true
139
+ });
140
+ const timeoutId = setTimeout(() => {
141
+ process.stderr.write('INTEROP: Process timeout reached.\n');
142
+ controller.abort();
143
+ }, timeoutDuration);
144
+ // --- Event Listeners (stdout, stderr, error, close, abort) ---
145
+ childProcess.stdout.on('data', (data) => {
146
+ const chunk = data.toString();
147
+ stdoutData += chunk;
148
+ process.stderr.write(`INTEROP stdout chunk: ${chunk.length > 50 ? chunk.substring(0, 50) + '...' : chunk}\n`);
149
+ });
150
+ childProcess.stderr.on('data', (data) => {
151
+ const chunk = data.toString();
152
+ stderrData += chunk;
153
+ // Check for specific error patterns
154
+ if (chunk.includes('--profile="profile name"')) {
155
+ process.stderr.write(`>>>> INTEROP STDERR DETECTED Profile Error Message: ${chunk}\n`);
156
+ }
157
+ else if (chunk.includes('is not recognized')) {
158
+ process.stderr.write(`>>>> INTEROP STDERR DETECTED 'not recognized' (shell issue?): ${chunk}\n`);
159
+ }
160
+ else if (chunk.includes('SyntaxErrorException')) {
161
+ process.stderr.write(`>>>> INTEROP STDERR DETECTED Syntax Error: ${chunk}\n`);
162
+ } // More specific check
163
+ else {
164
+ process.stderr.write(`INTEROP stderr chunk: ${chunk}\n`);
165
+ }
166
+ });
167
+ childProcess.on('error', (spawnError) => {
168
+ var _a;
169
+ clearTimeout(timeoutId);
170
+ process.stderr.write(`INTEROP SPAWN ERROR (shell:true): ${spawnError.message}\n`);
171
+ resolve({ code: (_a = spawnError.errno) !== null && _a !== void 0 ? _a : 1, stdout: stdoutData, stderr: stderrData, error: spawnError });
172
+ });
173
+ childProcess.on('close', (code) => {
174
+ clearTimeout(timeoutId);
175
+ process.stderr.write(`INTEROP: Process closed code: ${code}\n`);
176
+ resolve({ code: code, stdout: stdoutData, stderr: stderrData });
177
+ });
178
+ timeoutSignal.addEventListener('abort', () => {
179
+ process.stderr.write('INTEROP: Abort signal received, attempting to kill process.\n');
180
+ if (!childProcess.killed) {
181
+ if (!childProcess.kill('SIGTERM')) { // Try graceful termination first
182
+ process.stderr.write('INTEROP: SIGTERM failed, attempting SIGKILL in 2s.\n');
183
+ setTimeout(() => { if (!childProcess.killed)
184
+ childProcess.kill('SIGKILL'); }, 2000);
185
+ }
186
+ else {
187
+ process.stderr.write('INTEROP: SIGTERM sent.\n');
188
+ }
189
+ }
190
+ resolve({ code: null, stdout: stdoutData, stderr: stderrData + "\nTIMEOUT: Process aborted due to timeout." });
191
+ }, { once: true });
192
+ // --- End Event Listeners ---
193
+ });
194
+ output = spawnResult.stdout;
195
+ stderrOutput = spawnResult.stderr;
196
+ exitCode = spawnResult.code;
197
+ // --- Success Determination Logic ---
198
+ success = false; // Assume failure unless proven otherwise
199
+ if (spawnResult.error) {
200
+ process.stderr.write(`INTEROP: Failure determined by spawn error: ${spawnResult.error.message}\n`);
201
+ if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
202
+ stderrOutput = `SCRIPT_ERROR: Spawn failed: ${spawnResult.error.message}\n${stderrOutput}`;
203
+ }
204
+ else if (stderrOutput.includes('is not recognized as an internal or external command')) {
205
+ process.stderr.write("INTEROP: Failure determined by 'not recognized' error in stderr (shell:true quoting issue likely).\n");
206
+ if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
207
+ stderrOutput = `SCRIPT_ERROR: Shell execution failed: ${stderrOutput}`;
208
+ }
209
+ else if (stderrOutput.includes('--profile="profile name"')) {
210
+ process.stderr.write("INTEROP: Failure determined by CODESYS profile error message in stderr.\n");
211
+ if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
212
+ stderrOutput = `SCRIPT_ERROR: ${stderrOutput}`;
213
+ }
214
+ else if (stderrOutput.includes('SyntaxErrorException')) { // Check for syntax error specifically
215
+ process.stderr.write("INTEROP: Failure determined by CODESYS Script Syntax Error.\n");
216
+ if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
217
+ stderrOutput = `SCRIPT_ERROR: ${stderrOutput}`; // Include the syntax error details
218
+ }
219
+ else {
220
+ // No spawn error, no shell error, no profile error, no syntax error -> check markers/exit code
221
+ process.stderr.write(`INTEROP: Checking markers/exit code (Code: ${exitCode})...\n`);
222
+ if (output.includes(SCRIPT_SUCCESS_MARKER) || stderrOutput.includes(SCRIPT_SUCCESS_MARKER)) {
223
+ success = true;
224
+ process.stderr.write("INTEROP: Success determined by SUCCESS marker.\n");
225
+ }
226
+ else if (output.includes(SCRIPT_ERROR_MARKER) || stderrOutput.includes(SCRIPT_ERROR_MARKER)) {
227
+ success = false; // Explicit error marker found
228
+ process.stderr.write("INTEROP: Failure determined by ERROR marker.\n");
229
+ }
230
+ else {
231
+ // No markers found, rely solely on exit code
232
+ success = exitCode === 0;
233
+ if (success) {
234
+ process.stderr.write(`INTEROP: Success determined by exit code 0 (no markers found).\n`);
235
+ }
236
+ else {
237
+ process.stderr.write(`INTEROP: Failure determined by non-zero exit code ${exitCode} (no markers found).\n`);
238
+ // Add generic failure message if stderr doesn't already contain SCRIPT_ERROR
239
+ if (!stderrOutput.includes(SCRIPT_ERROR_MARKER))
240
+ stderrOutput = `SCRIPT_ERROR: Process failed with exit code ${exitCode} (no markers found).\n${stderrOutput}`;
241
+ }
242
+ }
243
+ }
244
+ // --- End Success Determination ---
245
+ }
246
+ catch (error) {
247
+ process.stderr.write(`INTEROP: Error during setup: ${error.message}\n${error.stack}\n`);
248
+ stderrOutput = `SCRIPT_ERROR: Failed during script execution setup: ${error.message}`;
249
+ success = false;
250
+ }
251
+ finally {
252
+ // Cleanup: Attempt to delete the temporary script file
253
+ try {
254
+ yield (0, promises_1.unlink)(tempFilePath);
255
+ process.stderr.write(`INTEROP: Temp script deleted: ${tempFilePath}\n`);
256
+ }
257
+ catch (cleanupError) {
258
+ process.stderr.write(`INTEROP: Failed to delete temp file ${tempFilePath}: ${cleanupError.message}\n`);
259
+ if (success)
260
+ stderrOutput += `\nWARNING: Failed to delete temporary script file ${tempFilePath}. ${cleanupError.message}`;
261
+ }
262
+ }
263
+ // Final output processing
264
+ // Combine stderr and stdout only on failure to preserve clean success output
265
+ const finalOutput = success ? output : `${stderrOutput}\n${output}`.trim();
266
+ process.stderr.write(`INTEROP: Final Success: ${success}\n`);
267
+ process.stderr.write(`INTEROP: Final Output Length: ${finalOutput.length}\n---\n`);
268
+ return { success, output: finalOutput };
269
+ });
270
+ }
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.registerResources = registerResources;
46
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
47
+ const codesys_interop_1 = require("../codesys_interop");
48
+ const templates_1 = require("../templates");
49
+ const path = __importStar(require("path"));
50
+ function registerResources(server, config) {
51
+ const WORKSPACE_DIR = config.workspaceDir;
52
+ const codesysExePath = config.codesysPath;
53
+ const codesysProfileName = config.profileName;
54
+ console.error("HANDLERS: Registering Resources...");
55
+ // --- Resources ---
56
+ server.resource("project-status", "codesys://project/status", (uri) => __awaiter(this, void 0, void 0, function* () {
57
+ var _a, _b, _c, _d, _e;
58
+ console.error(`SERVER.TS Resource request: ${uri.href}`);
59
+ try {
60
+ const result = yield (0, codesys_interop_1.executeCodesysScript)(templates_1.CHECK_STATUS_SCRIPT, codesysExePath, codesysProfileName);
61
+ const outputLines = result.output.split(/[\r\n]+/).filter(line => line.trim());
62
+ const statusData = {};
63
+ outputLines.forEach(line => { const match = line.match(/^([^:]+):\s*(.*)$/); if (match) {
64
+ statusData[match[1].trim()] = match[2].trim();
65
+ } });
66
+ const statusText = `CODESYS Status:\n - Scripting OK: ${(_a = statusData['Scripting OK']) !== null && _a !== void 0 ? _a : 'Unknown'}\n - Project Open: ${(_b = statusData['Project Open']) !== null && _b !== void 0 ? _b : 'Unknown'}\n - Project Name: ${(_c = statusData['Project Name']) !== null && _c !== void 0 ? _c : 'Unknown'}\n - Project Path: ${(_d = statusData['Project Path']) !== null && _d !== void 0 ? _d : 'N/A'}`;
67
+ const isError = !result.success || ((_e = statusData['Scripting OK']) === null || _e === void 0 ? void 0 : _e.toLowerCase()) !== 'true';
68
+ // **** RETURN MCP STRUCTURE ****
69
+ return { contents: [{ uri: uri.href, text: statusText, contentType: "text/plain" }], isError: isError };
70
+ }
71
+ catch (error) {
72
+ console.error(`Error resource ${uri.href}:`, error);
73
+ // **** RETURN MCP STRUCTURE ****
74
+ return { contents: [{ uri: uri.href, text: `Failed status script: ${error.message}`, contentType: "text/plain" }], isError: true };
75
+ }
76
+ }));
77
+ // *** DEFINE TEMPLATES ***
78
+ const projectStructureTemplate = new mcp_js_1.ResourceTemplate("codesys://project/{+project_path}/structure", { list: undefined });
79
+ const pouCodeTemplate = new mcp_js_1.ResourceTemplate("codesys://project/{+project_path}/pou/{+pou_path}/code", { list: undefined });
80
+ // *** END DEFINE TEMPLATES ***
81
+ server.resource("project-structure", projectStructureTemplate, (uri, params) => __awaiter(this, void 0, void 0, function* () {
82
+ // *** DEFINE VARIABLES (like projectPath) ***
83
+ const projectPathParam = params.project_path;
84
+ if (typeof projectPathParam !== 'string') {
85
+ return { contents: [{ uri: uri.href, text: `Error: Invalid project path type (${typeof projectPathParam}).`, contentType: "text/plain" }], isError: true };
86
+ }
87
+ const projectPath = projectPathParam; // Define projectPath
88
+ if (!projectPath) {
89
+ return { contents: [{ uri: uri.href, text: "Error: Project path missing.", contentType: "text/plain" }], isError: true };
90
+ }
91
+ // *** END DEFINE VARIABLES ***
92
+ console.error(`Resource request: project structure for ${projectPath}`);
93
+ try {
94
+ const absoluteProjPath = path.normalize(path.isAbsolute(projectPath) ? projectPath : path.join(WORKSPACE_DIR, projectPath));
95
+ const escapedPathForPython = absoluteProjPath.replace(/\\/g, '\\\\');
96
+ // *** DEFINE scriptContent ***
97
+ const scriptContent = templates_1.GET_PROJECT_STRUCTURE_SCRIPT_TEMPLATE.replace("{PROJECT_FILE_PATH}", escapedPathForPython);
98
+ // *** END DEFINE scriptContent ***
99
+ const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName);
100
+ let structureText = `Error retrieving structure for ${absoluteProjPath}.\n\n${result.output}`;
101
+ let isError = !result.success;
102
+ if (result.success && result.output.includes("SCRIPT_SUCCESS")) {
103
+ const startMarker = "--- PROJECT STRUCTURE START ---";
104
+ const endMarker = "--- PROJECT STRUCTURE END ---";
105
+ const startIndex = result.output.indexOf(startMarker);
106
+ const endIndex = result.output.indexOf(endMarker);
107
+ if (startIndex !== -1 && endIndex !== -1 && startIndex < endIndex) {
108
+ structureText = result.output.substring(startIndex + startMarker.length, endIndex).replace(/\\n/g, '\n').trim();
109
+ }
110
+ else {
111
+ console.error("Error: Could not find structure markers in script output.");
112
+ structureText = `Could not parse structure markers in output for ${absoluteProjPath}.\n\nOutput:\n${result.output}`;
113
+ isError = true;
114
+ }
115
+ }
116
+ else {
117
+ isError = true;
118
+ }
119
+ // **** RETURN MCP STRUCTURE ****
120
+ return { contents: [{ uri: uri.href, text: structureText, contentType: "text/plain" }], isError: isError };
121
+ }
122
+ catch (error) {
123
+ console.error(`Error getting structure ${uri.href}:`, error);
124
+ // **** RETURN MCP STRUCTURE ****
125
+ return { contents: [{ uri: uri.href, text: `Failed structure script for '${projectPath}': ${error.message}`, contentType: "text/plain" }], isError: true };
126
+ }
127
+ }));
128
+ server.resource("pou-code", pouCodeTemplate, (uri, params) => __awaiter(this, void 0, void 0, function* () {
129
+ // *** DEFINE VARIABLES (like projectPath, pouPath) ***
130
+ const projectPathParam = params.project_path;
131
+ const pouPathParam = params.pou_path;
132
+ if (typeof projectPathParam !== 'string' || typeof pouPathParam !== 'string') {
133
+ return { contents: [{ uri: uri.href, text: "Error: Invalid project or POU path type.", contentType: "text/plain" }], isError: true };
134
+ }
135
+ const projectPath = projectPathParam; // Define projectPath
136
+ const pouPath = pouPathParam; // Define pouPath
137
+ if (!projectPath || !pouPath) {
138
+ return { contents: [{ uri: uri.href, text: "Error: Project or POU path missing.", contentType: "text/plain" }], isError: true };
139
+ }
140
+ // *** END DEFINE VARIABLES ***
141
+ console.error(`Resource request: POU code: Project='${projectPath}', POU='${pouPath}'`);
142
+ try {
143
+ const absoluteProjPath = path.normalize(path.isAbsolute(projectPath) ? projectPath : path.join(WORKSPACE_DIR, projectPath));
144
+ const sanitizedPouPath = String(pouPath).replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
145
+ const escapedProjPath = absoluteProjPath.replace(/\\/g, '\\\\');
146
+ // *** DEFINE scriptContent ***
147
+ let scriptContent = templates_1.GET_POU_CODE_SCRIPT_TEMPLATE.replace("{PROJECT_FILE_PATH}", escapedProjPath);
148
+ scriptContent = scriptContent.replace("{POU_FULL_PATH}", sanitizedPouPath);
149
+ // *** END DEFINE scriptContent ***
150
+ const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName);
151
+ let codeText = `Error retrieving code for object '${sanitizedPouPath}' in project '${absoluteProjPath}'.\n\n${result.output}`;
152
+ let isError = !result.success;
153
+ if (result.success && result.output.includes("SCRIPT_SUCCESS")) {
154
+ // ... (marker parsing logic using new markers) ...
155
+ const declStartMarker = "### POU DECLARATION START ###";
156
+ const declEndMarker = "### POU DECLARATION END ###";
157
+ const implStartMarker = "### POU IMPLEMENTATION START ###";
158
+ const implEndMarker = "### POU IMPLEMENTATION END ###";
159
+ const declStartIdx = result.output.indexOf(declStartMarker);
160
+ const declEndIdx = result.output.indexOf(declEndMarker);
161
+ const implStartIdx = result.output.indexOf(implStartMarker);
162
+ const implEndIdx = result.output.indexOf(implEndMarker);
163
+ let declaration = "/* Declaration not found in output */";
164
+ let implementation = "/* Implementation not found in output */";
165
+ if (declStartIdx !== -1 && declEndIdx !== -1 && declStartIdx < declEndIdx) {
166
+ declaration = result.output.substring(declStartIdx + declStartMarker.length, declEndIdx).replace(/\\n/g, '\n').trim();
167
+ }
168
+ else {
169
+ console.error(`WARN: Declaration markers not found correctly for ${sanitizedPouPath}`);
170
+ }
171
+ if (implStartIdx !== -1 && implEndIdx !== -1 && implStartIdx < implEndIdx) {
172
+ implementation = result.output.substring(implStartIdx + implStartMarker.length, implEndIdx).replace(/\\n/g, '\n').trim();
173
+ }
174
+ else {
175
+ console.error(`WARN: Implementation markers not found correctly for ${sanitizedPouPath}`);
176
+ }
177
+ codeText = `// ----- Declaration -----\n${declaration}\n\n// ----- Implementation -----\n${implementation}`;
178
+ // *** END MARKER PARSING ***
179
+ }
180
+ else {
181
+ isError = true;
182
+ }
183
+ // **** RETURN MCP STRUCTURE ****
184
+ return { contents: [{ uri: uri.href, text: codeText, contentType: "text/plain" }], isError: isError };
185
+ }
186
+ catch (error) {
187
+ console.error(`Error getting POU code ${uri.href}:`, error);
188
+ // **** RETURN MCP STRUCTURE ****
189
+ return { contents: [{ uri: uri.href, text: `Failed POU code script for '${pouPath}' in '${projectPath}': ${error.message}`, contentType: "text/plain" }], isError: true };
190
+ }
191
+ }));
192
+ // --- End Resources ---
193
+ }