@codesys/mcp-toolkit 1.2.0 → 1.2.4

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.
@@ -58,9 +58,19 @@ const os = __importStar(require("os"));
58
58
  const fs = __importStar(require("fs"));
59
59
  const events_1 = require("events");
60
60
  const uuid_1 = require("uuid");
61
+ const scriptLoader_1 = require("./scriptLoader");
61
62
  // Default timeout values
62
63
  const DEFAULT_SCRIPT_TIMEOUT = 60000; // 60 seconds
63
64
  const DEFAULT_SESSION_IDLE_TIMEOUT = 300000; // 5 minutes
65
+ const DEFAULT_SESSION_POLL_INTERVAL = 1000; // 1 second
66
+ // Session directory and pipe names
67
+ const SESSION_DIR = path.join(os.tmpdir(), 'codesys_session');
68
+ const COMMAND_PIPE = 'command_pipe';
69
+ const RESULT_PIPE = 'result_pipe';
70
+ const STATE_FILE = 'session_state.json';
71
+ // Define expected success/error markers from the Python scripts
72
+ const SCRIPT_SUCCESS_MARKER = 'SCRIPT_SUCCESS';
73
+ const SCRIPT_ERROR_MARKER = 'SCRIPT_ERROR';
64
74
  // Event names
65
75
  exports.SESSION_EVENTS = {
66
76
  STARTED: 'session:started',
@@ -109,6 +119,12 @@ class CodesysSessionManager extends events_1.EventEmitter {
109
119
  this.codesysExePath = codesysExePath;
110
120
  this.codesysProfileName = codesysProfileName;
111
121
  this.idleTimeoutMs = idleTimeoutMs;
122
+ this.sessionId = (0, uuid_1.v4)();
123
+ // Set up session directory and pipes
124
+ this.sessionDir = path.join(SESSION_DIR, this.sessionId);
125
+ this.commandPipePath = path.join(this.sessionDir, COMMAND_PIPE);
126
+ this.resultPipePath = path.join(this.sessionDir, RESULT_PIPE);
127
+ this.stateFilePath = path.join(this.sessionDir, STATE_FILE);
112
128
  // Log status changes through event emitter
113
129
  this.on(exports.SESSION_EVENTS.STARTED, () => {
114
130
  console.error(`SESSION_MANAGER: Session started successfully`);
@@ -134,6 +150,70 @@ class CodesysSessionManager extends events_1.EventEmitter {
134
150
  getState() {
135
151
  return this.sessionState;
136
152
  }
153
+ /**
154
+ * Get the session directory
155
+ */
156
+ getSessionDir() {
157
+ return this.sessionDir;
158
+ }
159
+ /**
160
+ * Get the session ID
161
+ */
162
+ getSessionId() {
163
+ return this.sessionId;
164
+ }
165
+ /**
166
+ * Check if the session is active
167
+ * @returns Promise that resolves to true if the session is active
168
+ */
169
+ isSessionActive() {
170
+ return __awaiter(this, void 0, void 0, function* () {
171
+ try {
172
+ // If our internal state is not running, return false immediately
173
+ if (this.sessionState !== exports.SessionState.RUNNING && this.sessionState !== exports.SessionState.BUSY) {
174
+ return false;
175
+ }
176
+ // Check if session directory exists
177
+ if (!fs.existsSync(this.sessionDir)) {
178
+ return false;
179
+ }
180
+ // Check if state file exists
181
+ if (!fs.existsSync(this.stateFilePath)) {
182
+ return false;
183
+ }
184
+ // Request status from the running session
185
+ const statusResult = yield this.executeScript('print("SCRIPT_SUCCESS: Session is active")', 5000);
186
+ return statusResult.success;
187
+ }
188
+ catch (error) {
189
+ console.error(`SESSION_MANAGER: Error checking session status: ${error}`);
190
+ return false;
191
+ }
192
+ });
193
+ }
194
+ /**
195
+ * Get detailed session information
196
+ * @returns Promise that resolves to session information
197
+ */
198
+ getSessionInfo() {
199
+ return __awaiter(this, void 0, void 0, function* () {
200
+ try {
201
+ // Check if session is active
202
+ const isActive = yield this.isSessionActive();
203
+ if (!isActive) {
204
+ return null;
205
+ }
206
+ // Read the state file
207
+ const stateContent = yield (0, promises_1.readFile)(this.stateFilePath, 'utf8');
208
+ const stateData = JSON.parse(stateContent);
209
+ return stateData;
210
+ }
211
+ catch (error) {
212
+ console.error(`SESSION_MANAGER: Error getting session info: ${error}`);
213
+ return null;
214
+ }
215
+ });
216
+ }
137
217
  /**
138
218
  * Start a CODESYS session
139
219
  * @returns Promise that resolves when the session is started
@@ -145,55 +225,122 @@ class CodesysSessionManager extends events_1.EventEmitter {
145
225
  return Promise.resolve(); // Session already running
146
226
  }
147
227
  this.sessionState = exports.SessionState.STARTING;
148
- return new Promise((resolve, reject) => {
228
+ try {
229
+ console.error(`SESSION_MANAGER: Starting CODESYS session with profile: ${this.codesysProfileName}`);
230
+ // Create session directory if it doesn't exist
231
+ if (!fs.existsSync(SESSION_DIR)) {
232
+ fs.mkdirSync(SESSION_DIR, { recursive: true });
233
+ }
234
+ // Create our specific session directory
235
+ if (!fs.existsSync(this.sessionDir)) {
236
+ fs.mkdirSync(this.sessionDir, { recursive: true });
237
+ }
238
+ // Create initial state file
239
+ const initialState = {
240
+ sessionId: this.sessionId,
241
+ status: exports.SessionState.STARTING,
242
+ startTime: new Date().toISOString()
243
+ };
244
+ yield (0, promises_1.writeFile)(this.stateFilePath, JSON.stringify(initialState, null, 2), 'utf8');
245
+ // Load the start_session script template
246
+ const scriptTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('start_session');
247
+ // Interpolate parameters
248
+ const scriptContent = (0, scriptLoader_1.interpolateScript)(scriptTemplate, {
249
+ SESSION_DIR: this.sessionDir,
250
+ COMMAND_PIPE: COMMAND_PIPE,
251
+ RESULT_PIPE: RESULT_PIPE,
252
+ STATE_FILE: STATE_FILE,
253
+ SESSION_ID: this.sessionId,
254
+ });
255
+ // Create a temp file for the script
256
+ const tempDir = os.tmpdir();
257
+ const tempFileName = `codesys_start_session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}.py`;
258
+ const tempFilePath = path.join(tempDir, tempFileName);
259
+ // Write the script to the temp file
260
+ yield (0, promises_1.writeFile)(tempFilePath, scriptContent, 'latin1');
261
+ // Prepare CODESYS process
262
+ const codesysDir = path.dirname(this.codesysExePath);
263
+ const quotedExePath = `"${this.codesysExePath}"`;
264
+ const profileArg = `--profile="${this.codesysProfileName}"`;
265
+ // Start CODESYS with the session script - in "visible" mode so we can see it's running
266
+ const fullCommandString = `${quotedExePath} ${profileArg} --runscript="${tempFilePath}"`;
267
+ // Create a modified environment with CODESYS bin dir in PATH
268
+ const spawnEnv = Object.assign({}, process.env);
269
+ const pathSeparator = os.platform() === 'win32' ? ';' : ':';
270
+ const originalPath = spawnEnv.PATH || '';
271
+ spawnEnv.PATH = `${codesysDir}${pathSeparator}${originalPath}`;
272
+ // Spawn the process
273
+ this.codesysProcess = (0, child_process_1.spawn)(fullCommandString, [], {
274
+ windowsHide: false, // Make visible to user
275
+ cwd: codesysDir,
276
+ env: spawnEnv,
277
+ shell: true
278
+ });
279
+ // Handle process events
280
+ this.codesysProcess.on('error', (error) => {
281
+ console.error(`SESSION_MANAGER: Error starting CODESYS: ${error.message}`);
282
+ this.emit(exports.SESSION_EVENTS.FAILED, error);
283
+ this.sessionState = exports.SessionState.ERROR;
284
+ });
285
+ this.codesysProcess.on('exit', (code) => {
286
+ console.error(`SESSION_MANAGER: CODESYS process exited with code ${code}`);
287
+ // Only change state if we're not already stopping
288
+ if (this.sessionState !== exports.SessionState.STOPPING && this.sessionState !== exports.SessionState.STOPPED) {
289
+ this.sessionState = exports.SessionState.ERROR;
290
+ this.emit(exports.SESSION_EVENTS.FAILED, new Error(`CODESYS process exited unexpectedly with code ${code}`));
291
+ }
292
+ });
293
+ // Wait for session to initialize by checking for the existence of pipes
294
+ const success = yield this.waitForSessionInitialization();
295
+ if (success) {
296
+ this.sessionState = exports.SessionState.RUNNING;
297
+ this.emit(exports.SESSION_EVENTS.STARTED);
298
+ this.resetIdleTimeout();
299
+ }
300
+ else {
301
+ throw new Error('Session failed to initialize within timeout');
302
+ }
303
+ }
304
+ catch (error) {
305
+ console.error(`SESSION_MANAGER: Error during session start: ${error.message}`);
306
+ this.sessionState = exports.SessionState.ERROR;
307
+ this.emit(exports.SESSION_EVENTS.FAILED, error);
308
+ throw error;
309
+ }
310
+ });
311
+ }
312
+ /**
313
+ * Wait for session initialization by checking for the existence of the pipes
314
+ * @param timeoutMs Timeout in milliseconds
315
+ * @returns Promise that resolves to true if session initialized successfully
316
+ */
317
+ waitForSessionInitialization() {
318
+ return __awaiter(this, arguments, void 0, function* (timeoutMs = 30000) {
319
+ const startTime = Date.now();
320
+ while (Date.now() - startTime < timeoutMs) {
149
321
  try {
150
- console.error(`SESSION_MANAGER: Starting CODESYS session with profile: ${this.codesysProfileName}`);
151
- // Create a keep-alive script that keeps CODESYS running
152
- const keepAliveScript = this.createKeepAliveScript();
153
- // Prepare CODESYS process
154
- const codesysDir = path.dirname(this.codesysExePath);
155
- const quotedExePath = `"${this.codesysExePath}"`;
156
- const profileArg = `--profile="${this.codesysProfileName}"`;
157
- // Start CODESYS with the keep-alive script - in "visible" mode so we can see it's running
158
- const fullCommandString = `${quotedExePath} ${profileArg} --runscript="${keepAliveScript}"`;
159
- // Create a modified environment with CODESYS bin dir in PATH
160
- const spawnEnv = Object.assign({}, process.env);
161
- const pathSeparator = os.platform() === 'win32' ? ';' : ':';
162
- const originalPath = spawnEnv.PATH || '';
163
- spawnEnv.PATH = `${codesysDir}${pathSeparator}${originalPath}`;
164
- // Spawn the process
165
- this.codesysProcess = (0, child_process_1.spawn)(fullCommandString, [], {
166
- windowsHide: false, // Make visible to user
167
- cwd: codesysDir,
168
- env: spawnEnv,
169
- shell: true
170
- });
171
- // Handle process events
172
- this.codesysProcess.on('error', (error) => {
173
- console.error(`SESSION_MANAGER: Error starting CODESYS: ${error.message}`);
174
- this.emit(exports.SESSION_EVENTS.FAILED, error);
175
- reject(error);
176
- });
177
- // Give it a small delay to start properly
178
- setTimeout(() => {
179
- if (this.codesysProcess && !this.codesysProcess.killed) {
180
- this.emit(exports.SESSION_EVENTS.STARTED);
181
- this.resetIdleTimeout();
182
- resolve();
322
+ // Check if pipes exist
323
+ const commandPipeExists = fs.existsSync(this.commandPipePath);
324
+ const resultPipeExists = fs.existsSync(this.resultPipePath);
325
+ if (commandPipeExists && resultPipeExists) {
326
+ // Read state file to check if session is ready
327
+ const stateContent = yield (0, promises_1.readFile)(this.stateFilePath, 'utf8');
328
+ const stateData = JSON.parse(stateContent);
329
+ if (stateData.status === exports.SessionState.RUNNING) {
330
+ console.error(`SESSION_MANAGER: Session initialized successfully`);
331
+ return true;
183
332
  }
184
- else {
185
- const error = new Error('CODESYS process failed to start');
186
- this.emit(exports.SESSION_EVENTS.FAILED, error);
187
- reject(error);
188
- }
189
- }, 2000); // 2 second delay to let CODESYS initialize
333
+ }
334
+ // Wait a bit before checking again
335
+ yield new Promise(resolve => setTimeout(resolve, DEFAULT_SESSION_POLL_INTERVAL));
190
336
  }
191
337
  catch (error) {
192
- console.error(`SESSION_MANAGER: Error during session start: ${error.message}`);
193
- this.emit(exports.SESSION_EVENTS.FAILED, error);
194
- reject(error);
338
+ console.error(`SESSION_MANAGER: Error checking session initialization: ${error}`);
339
+ // Continue waiting
195
340
  }
196
- });
341
+ }
342
+ console.error(`SESSION_MANAGER: Session initialization timed out after ${timeoutMs}ms`);
343
+ return false;
197
344
  });
198
345
  }
199
346
  /**
@@ -217,29 +364,123 @@ class CodesysSessionManager extends events_1.EventEmitter {
217
364
  pending.reject(new Error('Session was stopped while script was pending'));
218
365
  this.pendingScripts.delete(id);
219
366
  }
220
- return new Promise((resolve) => {
367
+ try {
368
+ // Check if we have a running session
369
+ if (fs.existsSync(this.sessionDir) && fs.existsSync(this.commandPipePath)) {
370
+ // Load the stop_session script template
371
+ const scriptTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('stop_session');
372
+ // Interpolate parameters
373
+ const scriptContent = (0, scriptLoader_1.interpolateScript)(scriptTemplate, {
374
+ SESSION_DIR: this.sessionDir,
375
+ COMMAND_PIPE: COMMAND_PIPE,
376
+ RESULT_PIPE: RESULT_PIPE,
377
+ STATE_FILE: STATE_FILE,
378
+ SESSION_ID: this.sessionId,
379
+ });
380
+ try {
381
+ // Execute the stop script
382
+ const tempDir = os.tmpdir();
383
+ const tempFileName = `codesys_stop_session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}.py`;
384
+ const tempFilePath = path.join(tempDir, tempFileName);
385
+ // Write the script to the temp file
386
+ yield (0, promises_1.writeFile)(tempFilePath, scriptContent, 'latin1');
387
+ // Prepare CODESYS process
388
+ const codesysDir = path.dirname(this.codesysExePath);
389
+ const quotedExePath = `"${this.codesysExePath}"`;
390
+ const profileArg = `--profile="${this.codesysProfileName}"`;
391
+ // Execute the stop script
392
+ const fullCommandString = `${quotedExePath} ${profileArg} --noUI --runscript="${tempFilePath}"`;
393
+ // Create a modified environment with CODESYS bin dir in PATH
394
+ const spawnEnv = Object.assign({}, process.env);
395
+ const pathSeparator = os.platform() === 'win32' ? ';' : ':';
396
+ const originalPath = spawnEnv.PATH || '';
397
+ spawnEnv.PATH = `${codesysDir}${pathSeparator}${originalPath}`;
398
+ // Execute the script
399
+ console.error('SESSION_MANAGER: Executing stop session script');
400
+ const stopProcess = (0, child_process_1.spawn)(fullCommandString, [], {
401
+ windowsHide: true,
402
+ cwd: codesysDir,
403
+ env: spawnEnv,
404
+ shell: true
405
+ });
406
+ yield new Promise((resolve) => {
407
+ stopProcess.on('exit', () => {
408
+ resolve();
409
+ });
410
+ // Set a timeout in case the stop script hangs
411
+ setTimeout(() => {
412
+ resolve();
413
+ }, 5000);
414
+ });
415
+ // Clean up the temp file
416
+ try {
417
+ yield (0, promises_1.unlink)(tempFilePath);
418
+ }
419
+ catch (error) {
420
+ console.error(`SESSION_MANAGER: Error deleting temp file ${tempFilePath}: ${error}`);
421
+ }
422
+ }
423
+ catch (error) {
424
+ console.error(`SESSION_MANAGER: Error executing stop script: ${error}`);
425
+ }
426
+ }
427
+ // If we have a process, kill it
221
428
  if (this.codesysProcess && !this.codesysProcess.killed) {
222
429
  console.error('SESSION_MANAGER: Stopping CODESYS process');
223
430
  // First try SIGTERM
224
431
  this.codesysProcess.kill('SIGTERM');
225
432
  // Set a timeout to force kill if needed
226
- setTimeout(() => {
227
- if (this.codesysProcess && !this.codesysProcess.killed) {
228
- console.error('SESSION_MANAGER: SIGTERM failed, using SIGKILL');
229
- this.codesysProcess.kill('SIGKILL');
230
- }
231
- this.codesysProcess = null;
232
- this.emit(exports.SESSION_EVENTS.STOPPED);
233
- resolve();
234
- }, 2000); // Give it 2 seconds to terminate gracefully
433
+ yield new Promise((resolve) => {
434
+ setTimeout(() => {
435
+ if (this.codesysProcess && !this.codesysProcess.killed) {
436
+ console.error('SESSION_MANAGER: SIGTERM failed, using SIGKILL');
437
+ this.codesysProcess.kill('SIGKILL');
438
+ }
439
+ resolve();
440
+ }, 2000); // Give it 2 seconds to terminate gracefully
441
+ });
442
+ }
443
+ // Clean up session directory
444
+ try {
445
+ if (fs.existsSync(this.sessionDir)) {
446
+ console.error(`SESSION_MANAGER: Cleaning up session directory: ${this.sessionDir}`);
447
+ // Use recursive: true once we're on Node.js 14+
448
+ this.recursiveRemoveDir(this.sessionDir);
449
+ }
450
+ }
451
+ catch (error) {
452
+ console.error(`SESSION_MANAGER: Error cleaning up session directory: ${error}`);
453
+ }
454
+ this.codesysProcess = null;
455
+ this.sessionState = exports.SessionState.STOPPED;
456
+ this.emit(exports.SESSION_EVENTS.STOPPED);
457
+ }
458
+ catch (error) {
459
+ console.error(`SESSION_MANAGER: Error during session stop: ${error}`);
460
+ this.sessionState = exports.SessionState.ERROR;
461
+ this.emit(exports.SESSION_EVENTS.FAILED, error);
462
+ }
463
+ });
464
+ }
465
+ /**
466
+ * Recursively remove a directory
467
+ * @param dirPath Directory path to remove
468
+ */
469
+ recursiveRemoveDir(dirPath) {
470
+ if (fs.existsSync(dirPath)) {
471
+ fs.readdirSync(dirPath).forEach((file) => {
472
+ const curPath = path.join(dirPath, file);
473
+ if (fs.lstatSync(curPath).isDirectory()) {
474
+ // Recursive call
475
+ this.recursiveRemoveDir(curPath);
235
476
  }
236
477
  else {
237
- this.codesysProcess = null;
238
- this.emit(exports.SESSION_EVENTS.STOPPED);
239
- resolve();
478
+ // Delete file
479
+ fs.unlinkSync(curPath);
240
480
  }
241
481
  });
242
- });
482
+ fs.rmdirSync(dirPath);
483
+ }
243
484
  }
244
485
  /**
245
486
  * Execute a script in the persistent CODESYS session
@@ -283,31 +524,35 @@ class CodesysSessionManager extends events_1.EventEmitter {
283
524
  this.resetIdleTimeout();
284
525
  // Mark the session as busy
285
526
  this.sessionState = exports.SessionState.BUSY;
527
+ // Check if the command pipe exists
528
+ if (!fs.existsSync(this.commandPipePath)) {
529
+ this.sessionState = exports.SessionState.ERROR;
530
+ throw new Error(`Command pipe not found at ${this.commandPipePath}`);
531
+ }
532
+ // Check if the result pipe exists
533
+ if (!fs.existsSync(this.resultPipePath)) {
534
+ this.sessionState = exports.SessionState.ERROR;
535
+ throw new Error(`Result pipe not found at ${this.resultPipePath}`);
536
+ }
286
537
  // Return a promise that will be resolved when script execution completes
287
538
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
288
539
  try {
289
- // Create a temp file for the script
290
- const tempDir = os.tmpdir();
291
- const tempFileName = `codesys_script_${Date.now()}_${Math.random().toString(36).substring(2, 9)}.py`;
292
- const tempFilePath = path.join(tempDir, tempFileName);
293
- // Write the script to the temp file
294
- yield (0, promises_1.writeFile)(tempFilePath, scriptContent, 'latin1');
295
- console.error(`SESSION_MANAGER: Script ${scriptId} created at ${tempFilePath}`);
540
+ console.error(`SESSION_MANAGER: Executing script ${scriptId}`);
296
541
  // Set up a timeout for this script execution
297
542
  const scriptTimeout = setTimeout(() => {
298
543
  if (this.pendingScripts.has(scriptId)) {
299
544
  const pending = this.pendingScripts.get(scriptId);
300
545
  this.pendingScripts.delete(scriptId);
301
546
  pending.reject(new Error(`Script execution timed out after ${timeoutMs}ms`));
547
+ // Check if we have more pending scripts
548
+ if (this.pendingScripts.size === 0) {
549
+ this.sessionState = exports.SessionState.RUNNING;
550
+ }
302
551
  }
303
552
  }, timeoutMs);
304
553
  // Add to pending scripts
305
554
  this.pendingScripts.set(scriptId, {
306
555
  resolve: (result) => {
307
- // Clean up the temp file
308
- (0, promises_1.unlink)(tempFilePath).catch(err => {
309
- console.error(`SESSION_MANAGER: Error deleting temp file ${tempFilePath}: ${err.message}`);
310
- });
311
556
  resolve(result);
312
557
  // Check if we have more pending scripts
313
558
  if (this.pendingScripts.size === 0) {
@@ -315,10 +560,6 @@ class CodesysSessionManager extends events_1.EventEmitter {
315
560
  }
316
561
  },
317
562
  reject: (error) => {
318
- // Clean up the temp file
319
- (0, promises_1.unlink)(tempFilePath).catch(err => {
320
- console.error(`SESSION_MANAGER: Error deleting temp file ${tempFilePath}: ${err.message}`);
321
- });
322
563
  reject(error);
323
564
  // Check if we have more pending scripts
324
565
  if (this.pendingScripts.size === 0) {
@@ -327,33 +568,100 @@ class CodesysSessionManager extends events_1.EventEmitter {
327
568
  },
328
569
  timeout: scriptTimeout
329
570
  });
330
- // TODO: Implement the actual execution of the script in the running CODESYS instance
331
- // For now, we'll just simulate it with a timeout
332
- setTimeout(() => {
333
- if (this.pendingScripts.has(scriptId)) {
334
- const pending = this.pendingScripts.get(scriptId);
335
- clearTimeout(pending.timeout);
336
- this.pendingScripts.delete(scriptId);
337
- // Return a mocked result
338
- const result = {
339
- id: scriptId,
340
- success: true,
341
- output: "SCRIPT_SUCCESS: This is a mock implementation of script execution!"
342
- };
343
- pending.resolve(result);
344
- }
345
- }, 1000); // 1 second delay for mock result
571
+ // Create the command to send
572
+ const command = {
573
+ id: scriptId,
574
+ script: scriptContent,
575
+ timestamp: Date.now()
576
+ };
577
+ // Send the command
578
+ const commandString = JSON.stringify(command) + '\n';
579
+ fs.writeFileSync(this.commandPipePath, commandString);
580
+ console.error(`SESSION_MANAGER: Command sent to pipe`);
581
+ // Start reading the result pipe
582
+ this.readResultPipe(scriptId);
346
583
  }
347
584
  catch (error) {
348
- reject(error);
349
- // Check if we have more pending scripts
585
+ console.error(`SESSION_MANAGER: Error executing script: ${error.message}`);
586
+ // Remove from pending scripts
587
+ if (this.pendingScripts.has(scriptId)) {
588
+ const pending = this.pendingScripts.get(scriptId);
589
+ clearTimeout(pending.timeout);
590
+ this.pendingScripts.delete(scriptId);
591
+ }
592
+ // Update session state if no more pending scripts
350
593
  if (this.pendingScripts.size === 0) {
351
594
  this.sessionState = exports.SessionState.RUNNING;
352
595
  }
596
+ reject(error);
353
597
  }
354
598
  }));
355
599
  });
356
600
  }
601
+ /**
602
+ * Read the result pipe for a specific script execution
603
+ * @param scriptId The script ID to read results for
604
+ */
605
+ readResultPipe(scriptId) {
606
+ console.error(`SESSION_MANAGER: Starting to read result pipe for script ${scriptId}`);
607
+ // We'll use a polling approach to read from the pipe
608
+ const pollInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
609
+ try {
610
+ if (!this.pendingScripts.has(scriptId)) {
611
+ console.error(`SESSION_MANAGER: Script ${scriptId} is no longer pending, stopping poll`);
612
+ clearInterval(pollInterval);
613
+ return;
614
+ }
615
+ // Check if the result pipe exists
616
+ if (!fs.existsSync(this.resultPipePath)) {
617
+ console.error(`SESSION_MANAGER: Result pipe not found at ${this.resultPipePath}`);
618
+ return;
619
+ }
620
+ // Read the result pipe
621
+ const resultContent = yield (0, promises_1.readFile)(this.resultPipePath, 'utf8');
622
+ // Process all lines
623
+ const lines = resultContent.split('\n');
624
+ for (const line of lines) {
625
+ if (!line.trim())
626
+ continue;
627
+ try {
628
+ const result = JSON.parse(line);
629
+ // Check if this result is for our script
630
+ if (result.id === scriptId) {
631
+ console.error(`SESSION_MANAGER: Found result for script ${scriptId}`);
632
+ // Remove from pending scripts
633
+ if (this.pendingScripts.has(scriptId)) {
634
+ const pending = this.pendingScripts.get(scriptId);
635
+ clearTimeout(pending.timeout);
636
+ this.pendingScripts.delete(scriptId);
637
+ // Resolve with the result
638
+ const scriptResult = {
639
+ id: scriptId,
640
+ success: result.success,
641
+ output: result.output
642
+ };
643
+ pending.resolve(scriptResult);
644
+ }
645
+ // Stop polling
646
+ clearInterval(pollInterval);
647
+ return;
648
+ }
649
+ }
650
+ catch (parseError) {
651
+ console.error(`SESSION_MANAGER: Error parsing result line: ${parseError}`);
652
+ // Continue with the next line
653
+ }
654
+ }
655
+ }
656
+ catch (error) {
657
+ console.error(`SESSION_MANAGER: Error reading result pipe: ${error}`);
658
+ }
659
+ }), 100); // Poll every 100ms
660
+ // Set a timeout to stop polling after the script timeout
661
+ setTimeout(() => {
662
+ clearInterval(pollInterval);
663
+ }, 60000); // 1 minute max polling time
664
+ }
357
665
  /**
358
666
  * Reset the idle timeout
359
667
  */
@@ -371,31 +679,5 @@ class CodesysSessionManager extends events_1.EventEmitter {
371
679
  }, this.idleTimeoutMs);
372
680
  }
373
681
  }
374
- /**
375
- * Create a keep-alive script that keeps CODESYS running
376
- * This is a placeholder for now - we'll need to implement
377
- * a proper mechanism to keep CODESYS running while allowing
378
- * additional scripts to be executed
379
- */
380
- createKeepAliveScript() {
381
- // This is a placeholder - we need a real implementation
382
- // that keeps CODESYS running and allows for executing other scripts
383
- const tempScript = `
384
- import time
385
-
386
- # This is a placeholder keep-alive script
387
- # In a real implementation, we would:
388
- # 1. Set up a communication channel (named pipe, socket, etc.)
389
- # 2. Listen for script execution requests
390
- # 3. Execute them and return results
391
-
392
- print("Keep-alive script running...")
393
-
394
- # Just keep running for now
395
- while True:
396
- time.sleep(1)
397
- `;
398
- return tempScript;
399
- }
400
682
  }
401
683
  exports.CodesysSessionManager = CodesysSessionManager;
@@ -1,4 +1,9 @@
1
- # CODESYS MCP Session Management
1
+ # CODESYS MCP Session Management (Not Yet Implemented)
2
+
3
+ > **IMPORTANT:** Session management is currently disabled in version 1.2.4.
4
+ > The `start_session` and `stop_session` commands exist but are non-functional.
5
+ > All operations will run in standard mode (starting and stopping CODESYS for each operation).
6
+ > This feature will be fully implemented in a future release.
2
7
 
3
8
  ## Overview
4
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codesys/mcp-toolkit",
3
- "version": "1.2.0",
3
+ "version": "1.2.4",
4
4
  "description": "Model Context Protocol (MCP) server for CODESYS automation platform",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
@@ -8,8 +8,9 @@
8
8
  },
9
9
  "scripts": {
10
10
  "test": "echo \"Error: no test specified\" && exit 1",
11
- "build": "tsc",
12
- "start": "node ./dist/server.js"
11
+ "build": "tsc && node scripts/build.js",
12
+ "start": "node ./dist/server.js",
13
+ "prepublishOnly": "npm run build"
13
14
  },
14
15
  "keywords": [
15
16
  "codesys",