@codesys/mcp-toolkit 1.2.1 → 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.
- package/CHANGELOG.md +16 -0
- package/dist/codesys_interop.js +143 -2
- package/dist/scriptLoader.js +14 -2
- package/dist/scripts/create_project.py +51 -5
- package/dist/scripts/session_status.py +168 -0
- package/dist/scripts/start_session.py +240 -0
- package/dist/scripts/stop_session.py +94 -0
- package/dist/sessionManager.js +399 -117
- package/docs/session_management.md +6 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ 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.2.4] - 2025-05-02
|
|
9
|
+
### Fixed
|
|
10
|
+
- Fully disabled session management to prevent operations appearing to succeed but not actually running
|
|
11
|
+
- Made it clearer that the persistent session feature is not yet implemented
|
|
12
|
+
|
|
13
|
+
## [1.2.3] - 2025-05-02
|
|
14
|
+
### Fixed
|
|
15
|
+
- Improved Windows path handling in Python scripts
|
|
16
|
+
- Added better error detection and reporting for file operations
|
|
17
|
+
- Enhanced debugging for file creation operations
|
|
18
|
+
|
|
19
|
+
## [1.2.2] - 2025-05-02
|
|
20
|
+
### Fixed
|
|
21
|
+
- Fixed issue where session management appeared to work but didn't create files
|
|
22
|
+
- Temporarily disabled incomplete session manager implementation
|
|
23
|
+
|
|
8
24
|
## [1.2.1] - 2025-05-02
|
|
9
25
|
### Fixed
|
|
10
26
|
- Fixed Python script templates not being included in the npm package
|
package/dist/codesys_interop.js
CHANGED
|
@@ -56,12 +56,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
56
56
|
};
|
|
57
57
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
58
|
exports.executeCodesysScript = executeCodesysScript;
|
|
59
|
+
exports.checkSessionStatus = checkSessionStatus;
|
|
60
|
+
exports.startSession = startSession;
|
|
61
|
+
exports.stopSession = stopSession;
|
|
59
62
|
const child_process_1 = require("child_process");
|
|
60
63
|
const promises_1 = require("fs/promises");
|
|
61
64
|
const path = __importStar(require("path"));
|
|
62
65
|
const os = __importStar(require("os"));
|
|
63
66
|
const fs = __importStar(require("fs")); // Import fs for existsSync check
|
|
64
|
-
//
|
|
67
|
+
// Session manager import
|
|
65
68
|
const sessionManager_1 = require("./sessionManager");
|
|
66
69
|
// Define expected success/error markers from the Python scripts
|
|
67
70
|
const SCRIPT_SUCCESS_MARKER = 'SCRIPT_SUCCESS';
|
|
@@ -88,12 +91,16 @@ function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName,
|
|
|
88
91
|
if (!fs.existsSync(codesysExePath))
|
|
89
92
|
throw new Error(`CODESYS executable not found at provided path: ${codesysExePath}`);
|
|
90
93
|
// --- End Pre-checks ---
|
|
94
|
+
// Enable or disable session management
|
|
95
|
+
const SESSION_FEATURE_DISABLED = false;
|
|
91
96
|
// --- Check if we should use the persistent session ---
|
|
92
|
-
if (sessionManager) {
|
|
97
|
+
if (!SESSION_FEATURE_DISABLED && sessionManager) {
|
|
93
98
|
const sessionState = sessionManager.getState();
|
|
99
|
+
// Check if the session is active
|
|
94
100
|
if (sessionState === sessionManager_1.SessionState.RUNNING || sessionState === sessionManager_1.SessionState.BUSY) {
|
|
95
101
|
process.stderr.write(`INTEROP: Using persistent session for script execution\n`);
|
|
96
102
|
try {
|
|
103
|
+
// Execute the script in the session
|
|
97
104
|
const result = yield sessionManager.executeScript(scriptContent);
|
|
98
105
|
return {
|
|
99
106
|
success: result.success,
|
|
@@ -106,10 +113,40 @@ function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName,
|
|
|
106
113
|
// Continue with the non-persistent approach as fallback
|
|
107
114
|
}
|
|
108
115
|
}
|
|
116
|
+
else if (sessionState === sessionManager_1.SessionState.STOPPED || sessionState === sessionManager_1.SessionState.ERROR) {
|
|
117
|
+
// Try to start the session if it's stopped or in error state
|
|
118
|
+
process.stderr.write(`INTEROP: Session is in ${sessionState} state. Attempting to start it.\n`);
|
|
119
|
+
try {
|
|
120
|
+
yield sessionManager.startSession();
|
|
121
|
+
const newState = sessionManager.getState();
|
|
122
|
+
if (newState === sessionManager_1.SessionState.RUNNING) {
|
|
123
|
+
process.stderr.write(`INTEROP: Session started successfully. Using it for script execution.\n`);
|
|
124
|
+
// Now that the session is running, execute the script
|
|
125
|
+
const result = yield sessionManager.executeScript(scriptContent);
|
|
126
|
+
return {
|
|
127
|
+
success: result.success,
|
|
128
|
+
output: result.output
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
process.stderr.write(`INTEROP: Failed to start session (state: ${newState}). Using one-time execution.\n`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
process.stderr.write(`INTEROP: Error starting session: ${error.message}\n`);
|
|
137
|
+
process.stderr.write(`INTEROP: Using one-time execution as fallback.\n`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
109
140
|
else {
|
|
110
141
|
process.stderr.write(`INTEROP: Session not in RUNNING/BUSY state (current: ${sessionState}). Using one-time execution.\n`);
|
|
111
142
|
}
|
|
112
143
|
}
|
|
144
|
+
else if (SESSION_FEATURE_DISABLED) {
|
|
145
|
+
process.stderr.write(`INTEROP: Session feature is disabled. Using one-time execution mode.\n`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
process.stderr.write(`INTEROP: No session manager provided. Using one-time execution mode.\n`);
|
|
149
|
+
}
|
|
113
150
|
// --- Standard one-time script execution ---
|
|
114
151
|
const tempDir = os.tmpdir();
|
|
115
152
|
const tempFileName = `codesys_script_${Date.now()}_${Math.random().toString(36).substring(2, 9)}.py`;
|
|
@@ -295,3 +332,107 @@ function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName,
|
|
|
295
332
|
return { success, output: finalOutput };
|
|
296
333
|
});
|
|
297
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Checks if a session is currently active
|
|
337
|
+
*
|
|
338
|
+
* @param codesysExePath The full path to the CODESYS.exe executable
|
|
339
|
+
* @param codesysProfileName The name of the CODESYS profile to use for scripting
|
|
340
|
+
* @param sessionManager The session manager instance
|
|
341
|
+
* @returns Promise resolving to an object with session status
|
|
342
|
+
*/
|
|
343
|
+
function checkSessionStatus(codesysExePath, codesysProfileName, sessionManager) {
|
|
344
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
345
|
+
if (!sessionManager) {
|
|
346
|
+
return { active: false, info: null };
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
// Check if session is active
|
|
350
|
+
const isActive = yield sessionManager.isSessionActive();
|
|
351
|
+
if (!isActive) {
|
|
352
|
+
return { active: false, info: null };
|
|
353
|
+
}
|
|
354
|
+
// Get session information
|
|
355
|
+
const sessionInfo = yield sessionManager.getSessionInfo();
|
|
356
|
+
return {
|
|
357
|
+
active: true,
|
|
358
|
+
info: sessionInfo
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
process.stderr.write(`INTEROP: Error checking session status: ${error.message}\n`);
|
|
363
|
+
return { active: false, info: null };
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Starts a new persistent CODESYS session
|
|
369
|
+
*
|
|
370
|
+
* @param codesysExePath The full path to the CODESYS.exe executable
|
|
371
|
+
* @param codesysProfileName The name of the CODESYS profile to use for scripting
|
|
372
|
+
* @param sessionManager The session manager instance
|
|
373
|
+
* @returns Promise resolving when the session has started
|
|
374
|
+
*/
|
|
375
|
+
function startSession(codesysExePath, codesysProfileName, sessionManager) {
|
|
376
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
377
|
+
if (!sessionManager) {
|
|
378
|
+
return { success: false, message: "No session manager provided" };
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
// Check if session is already active
|
|
382
|
+
const isActive = yield sessionManager.isSessionActive();
|
|
383
|
+
if (isActive) {
|
|
384
|
+
return { success: true, message: "Session is already active" };
|
|
385
|
+
}
|
|
386
|
+
// Start a new session
|
|
387
|
+
yield sessionManager.startSession();
|
|
388
|
+
// Check if session started successfully
|
|
389
|
+
const statusAfterStart = yield sessionManager.isSessionActive();
|
|
390
|
+
if (statusAfterStart) {
|
|
391
|
+
return { success: true, message: "Session started successfully" };
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
return { success: false, message: "Failed to start session" };
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
process.stderr.write(`INTEROP: Error starting session: ${error.message}\n`);
|
|
399
|
+
return { success: false, message: `Error starting session: ${error.message}` };
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Stops an active CODESYS session
|
|
405
|
+
*
|
|
406
|
+
* @param codesysExePath The full path to the CODESYS.exe executable
|
|
407
|
+
* @param codesysProfileName The name of the CODESYS profile to use for scripting
|
|
408
|
+
* @param sessionManager The session manager instance
|
|
409
|
+
* @returns Promise resolving when the session has stopped
|
|
410
|
+
*/
|
|
411
|
+
function stopSession(codesysExePath, codesysProfileName, sessionManager) {
|
|
412
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
413
|
+
if (!sessionManager) {
|
|
414
|
+
return { success: false, message: "No session manager provided" };
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
// Check if session is active
|
|
418
|
+
const isActive = yield sessionManager.isSessionActive();
|
|
419
|
+
if (!isActive) {
|
|
420
|
+
return { success: true, message: "No active session to stop" };
|
|
421
|
+
}
|
|
422
|
+
// Stop the session
|
|
423
|
+
yield sessionManager.stopSession();
|
|
424
|
+
// Verify session stopped
|
|
425
|
+
const statusAfterStop = yield sessionManager.isSessionActive();
|
|
426
|
+
if (!statusAfterStop) {
|
|
427
|
+
return { success: true, message: "Session stopped successfully" };
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
return { success: false, message: "Failed to stop session" };
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
process.stderr.write(`INTEROP: Error stopping session: ${error.message}\n`);
|
|
435
|
+
return { success: false, message: `Error stopping session: ${error.message}` };
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
}
|
package/dist/scriptLoader.js
CHANGED
|
@@ -121,8 +121,20 @@ function interpolateScript(template, params) {
|
|
|
121
121
|
let result = template;
|
|
122
122
|
// Replace each parameter in the template
|
|
123
123
|
for (const [key, value] of Object.entries(params)) {
|
|
124
|
-
//
|
|
125
|
-
|
|
124
|
+
// Handle Windows paths specially
|
|
125
|
+
let escapedValue;
|
|
126
|
+
// Check if this looks like a Windows path
|
|
127
|
+
if (typeof value === 'string' && (value.includes(':\\') || value.includes('\\'))) {
|
|
128
|
+
// For Windows paths, use a different approach with raw strings
|
|
129
|
+
console.error(`SCRIPT_LOADER: Detected Windows path: ${value}`);
|
|
130
|
+
// Use Python raw string format to avoid backslash interpretation issues
|
|
131
|
+
escapedValue = `r'${value.replace(/'/g, "\\'")}'`;
|
|
132
|
+
console.error(`SCRIPT_LOADER: Converted to Python raw string: ${escapedValue}`);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// For non-path values, just escape backslashes normally
|
|
136
|
+
escapedValue = String(value).replace(/\\/g, '\\\\');
|
|
137
|
+
}
|
|
126
138
|
// Replace {PARAM_NAME} with the escaped value
|
|
127
139
|
const paramPattern = new RegExp(`\\{${key}\\}`, 'g');
|
|
128
140
|
result = result.replace(paramPattern, escapedValue);
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import sys, scriptengine as script_engine, os, shutil, time, traceback
|
|
2
|
-
# Placeholders
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
# Placeholders - These might be Python raw strings (r'path') from the Node.js side
|
|
3
|
+
# So we need to handle them specially
|
|
4
|
+
TEMPLATE_PROJECT_PATH_RAW = '{TEMPLATE_PROJECT_PATH}' # Path to Standard.project
|
|
5
|
+
PROJECT_FILE_PATH_RAW = '{PROJECT_FILE_PATH}' # Path for the new project (Target Path)
|
|
6
|
+
|
|
7
|
+
# Process the paths - if they start with r' and end with ', extract the content
|
|
8
|
+
def extract_path(path_str):
|
|
9
|
+
if path_str.startswith("r'") and path_str.endswith("'"):
|
|
10
|
+
# This is a raw string from Node.js - extract the content
|
|
11
|
+
return path_str[2:-1]
|
|
12
|
+
return path_str
|
|
13
|
+
|
|
14
|
+
TEMPLATE_PROJECT_PATH = extract_path(TEMPLATE_PROJECT_PATH_RAW)
|
|
15
|
+
PROJECT_FILE_PATH = extract_path(PROJECT_FILE_PATH_RAW)
|
|
16
|
+
|
|
17
|
+
print("DEBUG: Processed TEMPLATE_PROJECT_PATH: %s" % TEMPLATE_PROJECT_PATH)
|
|
18
|
+
print("DEBUG: Processed PROJECT_FILE_PATH: %s" % PROJECT_FILE_PATH)
|
|
5
19
|
try:
|
|
6
20
|
print("DEBUG: Python script create_project (copy from template):")
|
|
7
21
|
print("DEBUG: Template Source = %s" % TEMPLATE_PROJECT_PATH)
|
|
@@ -12,12 +26,44 @@ try:
|
|
|
12
26
|
|
|
13
27
|
# 1. Copy the template project file to the new location
|
|
14
28
|
target_dir = os.path.dirname(PROJECT_FILE_PATH)
|
|
15
|
-
|
|
29
|
+
print("DEBUG: Target directory path: %s" % target_dir)
|
|
30
|
+
|
|
31
|
+
# More robust directory creation
|
|
32
|
+
if not os.path.exists(target_dir):
|
|
33
|
+
try:
|
|
34
|
+
print("DEBUG: Creating target directory: %s" % target_dir)
|
|
35
|
+
os.makedirs(target_dir, exist_ok=True)
|
|
36
|
+
print("DEBUG: Directory creation result - exists after creation: %s" % os.path.exists(target_dir))
|
|
37
|
+
except Exception as dir_err:
|
|
38
|
+
print("ERROR: Failed to create directory %s: %s" % (target_dir, dir_err))
|
|
39
|
+
raise
|
|
40
|
+
|
|
16
41
|
# Check if target file already exists
|
|
17
42
|
if os.path.exists(PROJECT_FILE_PATH): print("WARN: Target project file already exists, overwriting: %s" % PROJECT_FILE_PATH)
|
|
18
43
|
|
|
19
44
|
print("DEBUG: Copying '%s' to '%s'..." % (TEMPLATE_PROJECT_PATH, PROJECT_FILE_PATH))
|
|
20
|
-
|
|
45
|
+
try:
|
|
46
|
+
# Complete path diagnosis
|
|
47
|
+
print("DEBUG: Template path absolute: %s" % os.path.abspath(TEMPLATE_PROJECT_PATH))
|
|
48
|
+
print("DEBUG: Target path absolute: %s" % os.path.abspath(PROJECT_FILE_PATH))
|
|
49
|
+
print("DEBUG: Template exists: %s" % os.path.exists(TEMPLATE_PROJECT_PATH))
|
|
50
|
+
print("DEBUG: Target dir exists: %s" % os.path.exists(target_dir))
|
|
51
|
+
print("DEBUG: Target dir is writable: %s" % os.access(target_dir, os.W_OK))
|
|
52
|
+
|
|
53
|
+
# Perform the copy
|
|
54
|
+
shutil.copy2(TEMPLATE_PROJECT_PATH, PROJECT_FILE_PATH) # copy2 preserves metadata
|
|
55
|
+
|
|
56
|
+
# Verify copy succeeded
|
|
57
|
+
if os.path.exists(PROJECT_FILE_PATH):
|
|
58
|
+
print("DEBUG: File copy VERIFIED - file exists at destination")
|
|
59
|
+
print("DEBUG: File size at destination: %s bytes" % os.path.getsize(PROJECT_FILE_PATH))
|
|
60
|
+
else:
|
|
61
|
+
print("ERROR: Copy seemed to succeed but file does not exist at destination!")
|
|
62
|
+
raise IOError("File not found at destination after copy: %s" % PROJECT_FILE_PATH)
|
|
63
|
+
except Exception as copy_err:
|
|
64
|
+
print("ERROR: Failed to copy file: %s" % copy_err)
|
|
65
|
+
raise
|
|
66
|
+
|
|
21
67
|
print("DEBUG: File copy complete.")
|
|
22
68
|
|
|
23
69
|
# 2. Open the newly copied project file
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import sys, os, json, time, tempfile
|
|
2
|
+
|
|
3
|
+
# Define markers for script results
|
|
4
|
+
SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
|
|
5
|
+
SCRIPT_ERROR_MARKER = "SCRIPT_ERROR"
|
|
6
|
+
|
|
7
|
+
# Session state file and pipes
|
|
8
|
+
SESSION_DIR = os.path.join(tempfile.gettempdir(), "codesys_session")
|
|
9
|
+
SESSION_STATE_FILE = os.path.join(SESSION_DIR, "session_state.json")
|
|
10
|
+
COMMAND_PIPE = os.path.join(SESSION_DIR, "command_pipe")
|
|
11
|
+
RESULT_PIPE = os.path.join(SESSION_DIR, "result_pipe")
|
|
12
|
+
|
|
13
|
+
def check_session_status():
|
|
14
|
+
"""Check the status of a CODESYS session by reading the state file"""
|
|
15
|
+
try:
|
|
16
|
+
# Check if session directory exists
|
|
17
|
+
if not os.path.exists(SESSION_DIR):
|
|
18
|
+
return {
|
|
19
|
+
"session_exists": False,
|
|
20
|
+
"active": False,
|
|
21
|
+
"message": "No session directory found"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Check if state file exists
|
|
25
|
+
if not os.path.exists(SESSION_STATE_FILE):
|
|
26
|
+
return {
|
|
27
|
+
"session_exists": True,
|
|
28
|
+
"active": False,
|
|
29
|
+
"message": "Session directory exists but no state file found"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Read the state file
|
|
33
|
+
with open(SESSION_STATE_FILE, 'r') as f:
|
|
34
|
+
session_state = json.load(f)
|
|
35
|
+
|
|
36
|
+
# Check if pipes exist
|
|
37
|
+
pipes_exist = os.path.exists(COMMAND_PIPE) and os.path.exists(RESULT_PIPE)
|
|
38
|
+
|
|
39
|
+
# Get session duration if available
|
|
40
|
+
start_time = session_state.get("start_time", 0)
|
|
41
|
+
if start_time > 0:
|
|
42
|
+
current_time = time.time()
|
|
43
|
+
duration_secs = int(current_time - start_time)
|
|
44
|
+
hours, remainder = divmod(duration_secs, 3600)
|
|
45
|
+
minutes, seconds = divmod(remainder, 60)
|
|
46
|
+
duration = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
47
|
+
else:
|
|
48
|
+
duration = "unknown"
|
|
49
|
+
|
|
50
|
+
# Last update time
|
|
51
|
+
last_update = session_state.get("last_update", 0)
|
|
52
|
+
last_update_str = "never"
|
|
53
|
+
if last_update > 0:
|
|
54
|
+
seconds_since_update = int(time.time() - last_update)
|
|
55
|
+
if seconds_since_update < 60:
|
|
56
|
+
last_update_str = f"{seconds_since_update} seconds ago"
|
|
57
|
+
elif seconds_since_update < 3600:
|
|
58
|
+
last_update_str = f"{seconds_since_update // 60} minutes ago"
|
|
59
|
+
else:
|
|
60
|
+
last_update_str = f"{seconds_since_update // 3600} hours ago"
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
"session_exists": True,
|
|
64
|
+
"active": session_state.get("active", False),
|
|
65
|
+
"project_open": session_state.get("project_open", False),
|
|
66
|
+
"project_name": session_state.get("project_name", "unknown"),
|
|
67
|
+
"project_path": session_state.get("project_path", "unknown"),
|
|
68
|
+
"duration": duration,
|
|
69
|
+
"pipes_exist": pipes_exist,
|
|
70
|
+
"last_update": last_update_str,
|
|
71
|
+
"message": "Session information retrieved successfully"
|
|
72
|
+
}
|
|
73
|
+
except Exception as e:
|
|
74
|
+
return {
|
|
75
|
+
"session_exists": True,
|
|
76
|
+
"active": False,
|
|
77
|
+
"error": str(e),
|
|
78
|
+
"message": f"Error reading session state: {e}"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
def ping_session():
|
|
82
|
+
"""Attempt to ping the session with a simple command to verify it's responsive"""
|
|
83
|
+
try:
|
|
84
|
+
if not os.path.exists(COMMAND_PIPE) or not os.path.exists(RESULT_PIPE):
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
ping_command = {
|
|
88
|
+
"id": f"ping_{time.time()}",
|
|
89
|
+
"type": "execute_script",
|
|
90
|
+
"script": "print('SCRIPT_SUCCESS: Ping successful')"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Set a timeout for the ping operation
|
|
94
|
+
timeout = 5 # seconds
|
|
95
|
+
|
|
96
|
+
# Use non-blocking I/O to handle timeouts
|
|
97
|
+
import select
|
|
98
|
+
|
|
99
|
+
# Send the ping command
|
|
100
|
+
with open(COMMAND_PIPE, 'w') as cmd_pipe:
|
|
101
|
+
cmd_pipe.write(json.dumps(ping_command))
|
|
102
|
+
|
|
103
|
+
# Wait for the result pipe to be readable with timeout
|
|
104
|
+
res_fd = os.open(RESULT_PIPE, os.O_RDONLY | os.O_NONBLOCK)
|
|
105
|
+
ready, _, _ = select.select([res_fd], [], [], timeout)
|
|
106
|
+
|
|
107
|
+
if not ready:
|
|
108
|
+
os.close(res_fd)
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
# Read the result
|
|
112
|
+
result_json = os.read(res_fd, 4096).decode('utf-8').strip()
|
|
113
|
+
os.close(res_fd)
|
|
114
|
+
|
|
115
|
+
if result_json:
|
|
116
|
+
try:
|
|
117
|
+
result = json.loads(result_json)
|
|
118
|
+
return result.get("success", False)
|
|
119
|
+
except json.JSONDecodeError:
|
|
120
|
+
return False
|
|
121
|
+
else:
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
except Exception as e:
|
|
125
|
+
print(f"Error pinging session: {e}")
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
def main():
|
|
129
|
+
try:
|
|
130
|
+
# Get session status
|
|
131
|
+
status = check_session_status()
|
|
132
|
+
|
|
133
|
+
# Print status information
|
|
134
|
+
print(f"Session Exists: {status.get('session_exists', False)}")
|
|
135
|
+
print(f"Session Active: {status.get('active', False)}")
|
|
136
|
+
|
|
137
|
+
# If session exists, print more details
|
|
138
|
+
if status.get("session_exists", False):
|
|
139
|
+
if status.get("active", False):
|
|
140
|
+
# Try to ping the session
|
|
141
|
+
is_responsive = ping_session()
|
|
142
|
+
print(f"Session Responsive: {is_responsive}")
|
|
143
|
+
|
|
144
|
+
# Print project information if available
|
|
145
|
+
if "project_open" in status:
|
|
146
|
+
print(f"Project Open: {status['project_open']}")
|
|
147
|
+
if "project_name" in status:
|
|
148
|
+
print(f"Project Name: {status['project_name']}")
|
|
149
|
+
if "project_path" in status:
|
|
150
|
+
print(f"Project Path: {status['project_path']}")
|
|
151
|
+
if "duration" in status:
|
|
152
|
+
print(f"Session Duration: {status['duration']}")
|
|
153
|
+
if "last_update" in status:
|
|
154
|
+
print(f"Last Activity: {status['last_update']}")
|
|
155
|
+
else:
|
|
156
|
+
print("Session is not active (stopped or crashed)")
|
|
157
|
+
|
|
158
|
+
# Print the final result marker
|
|
159
|
+
print(f"{SCRIPT_SUCCESS_MARKER}: Session status check complete")
|
|
160
|
+
sys.exit(0)
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
print(f"Error checking session status: {e}")
|
|
164
|
+
print(f"{SCRIPT_ERROR_MARKER}: {str(e)}")
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
main()
|