@codesys/mcp-toolkit 1.4.0 → 1.4.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 +11 -6
- package/dist/codesys_interop.js +1 -1
- package/dist/scripts/session_status.py +26 -19
- package/dist/scripts/start_session.py +66 -43
- package/dist/scripts/stop_session.py +26 -19
- package/package.json +1 -1
- package/replacements_session_status.txt +16 -0
- package/scripts/fix_fstrings.py +76 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,14 +5,19 @@ 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.4.1] - 2025-05-02
|
|
9
|
+
### Fixed
|
|
10
|
+
- Fixed IronPython 2.7 compatibility for session management scripts
|
|
11
|
+
- Replaced f-strings with str.format() method calls for IronPython compatibility
|
|
12
|
+
- Added fallback import for json module (using simplejson for IronPython)
|
|
13
|
+
- Improved error handling for session scripts
|
|
14
|
+
- Re-enabled session management feature in codesys_interop.ts
|
|
15
|
+
|
|
8
16
|
## [1.4.0] - 2025-05-02
|
|
9
17
|
### Added
|
|
10
|
-
- Implemented
|
|
11
|
-
- Added
|
|
12
|
-
- Created
|
|
13
|
-
- Improved session reliability with health checks and automatic recovery
|
|
14
|
-
- Added session timeout after 10 minutes of inactivity
|
|
15
|
-
- Created detailed documentation for session management features
|
|
18
|
+
- Implemented initial framework for session management
|
|
19
|
+
- Added foundation for file-based IPC between Node.js and CODESYS instances
|
|
20
|
+
- Created documentation structure for session management capabilities
|
|
16
21
|
|
|
17
22
|
## [1.3.0] - 2025-05-02
|
|
18
23
|
### Changed
|
package/dist/codesys_interop.js
CHANGED
|
@@ -92,7 +92,7 @@ function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName,
|
|
|
92
92
|
throw new Error(`CODESYS executable not found at provided path: ${codesysExePath}`);
|
|
93
93
|
// --- End Pre-checks ---
|
|
94
94
|
// Enable or disable session management
|
|
95
|
-
const SESSION_FEATURE_DISABLED = false;
|
|
95
|
+
const SESSION_FEATURE_DISABLED = false; // Session feature enabled
|
|
96
96
|
// --- Check if we should use the persistent session ---
|
|
97
97
|
if (!SESSION_FEATURE_DISABLED && sessionManager) {
|
|
98
98
|
const sessionState = sessionManager.getState();
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import sys, os,
|
|
1
|
+
import sys, os, time, traceback
|
|
2
2
|
import tempfile
|
|
3
3
|
|
|
4
|
+
# Import JSON handling for IronPython 2.7 compatibility
|
|
5
|
+
try:
|
|
6
|
+
import json
|
|
7
|
+
except ImportError:
|
|
8
|
+
# For IronPython 2.7, we need to use simplejson instead
|
|
9
|
+
import simplejson as json
|
|
10
|
+
|
|
4
11
|
# Define markers for script results
|
|
5
12
|
SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
|
|
6
13
|
SCRIPT_ERROR_MARKER = "SCRIPT_ERROR"
|
|
@@ -17,12 +24,12 @@ def check_session_status():
|
|
|
17
24
|
try:
|
|
18
25
|
# Check if session directory exists
|
|
19
26
|
if not os.path.exists(SESSION_DIR):
|
|
20
|
-
print(
|
|
27
|
+
print("{0}: No session active. Session directory not found.".format(SCRIPT_SUCCESS_MARKER))
|
|
21
28
|
return False
|
|
22
29
|
|
|
23
30
|
# Check if session state file exists
|
|
24
31
|
if not os.path.exists(SESSION_STATE_FILE):
|
|
25
|
-
print(
|
|
32
|
+
print("{0}: No session active. Session state file not found.".format(SCRIPT_SUCCESS_MARKER))
|
|
26
33
|
return False
|
|
27
34
|
|
|
28
35
|
# Read session state
|
|
@@ -30,7 +37,7 @@ def check_session_status():
|
|
|
30
37
|
with open(SESSION_STATE_FILE, 'r') as f:
|
|
31
38
|
session_state = json.load(f)
|
|
32
39
|
except json.JSONDecodeError:
|
|
33
|
-
print(
|
|
40
|
+
print("{0}: Invalid session state file".format(SCRIPT_ERROR_MARKER))
|
|
34
41
|
return False
|
|
35
42
|
|
|
36
43
|
# Check if session is active
|
|
@@ -39,7 +46,7 @@ def check_session_status():
|
|
|
39
46
|
|
|
40
47
|
# If the status indicates the session is not active, return immediately
|
|
41
48
|
if not active or status in ['stopped', 'error', 'stopping', 'timeout']:
|
|
42
|
-
print(
|
|
49
|
+
print("{0}: Session is not active (Status: {1})".format(SCRIPT_SUCCESS_MARKER, status))
|
|
43
50
|
return False
|
|
44
51
|
|
|
45
52
|
# Check session lock file to see if it's been updated recently
|
|
@@ -53,10 +60,10 @@ def check_session_status():
|
|
|
53
60
|
|
|
54
61
|
# If lock file hasn't been updated in 30 seconds, session might be dead
|
|
55
62
|
if time_diff > 30:
|
|
56
|
-
print(
|
|
63
|
+
print("WARNING: Session lock file is stale by {0:.1f} seconds".format(time_diff))
|
|
57
64
|
lock_is_stale = True
|
|
58
65
|
except Exception as e:
|
|
59
|
-
print(
|
|
66
|
+
print("Error reading lock file: {0}".format(e))
|
|
60
67
|
lock_is_stale = True
|
|
61
68
|
else:
|
|
62
69
|
print("WARNING: Session lock file not found")
|
|
@@ -74,7 +81,7 @@ def check_session_status():
|
|
|
74
81
|
|
|
75
82
|
# Create ping command
|
|
76
83
|
ping_command = {
|
|
77
|
-
"id":
|
|
84
|
+
"id": "ping_{0}".format(int(time.time())),
|
|
78
85
|
"type": "ping", # Use specific ping command type
|
|
79
86
|
"timestamp": time.time()
|
|
80
87
|
}
|
|
@@ -110,11 +117,11 @@ def check_session_status():
|
|
|
110
117
|
print("Ping successful, session is responsive")
|
|
111
118
|
break
|
|
112
119
|
except Exception as e:
|
|
113
|
-
print(
|
|
120
|
+
print("Error reading result: {0}".format(e))
|
|
114
121
|
time.sleep(0.5)
|
|
115
122
|
|
|
116
123
|
if not ping_success:
|
|
117
|
-
print(
|
|
124
|
+
print("WARNING: Session did not respond to ping within {0} seconds".format(timeout))
|
|
118
125
|
|
|
119
126
|
# Reset command file since ping failed
|
|
120
127
|
try:
|
|
@@ -129,11 +136,11 @@ def check_session_status():
|
|
|
129
136
|
|
|
130
137
|
print("Reset command file after failed ping")
|
|
131
138
|
except Exception as e:
|
|
132
|
-
print(
|
|
139
|
+
print("Error resetting command file: {0}".format(e))
|
|
133
140
|
else:
|
|
134
141
|
print("WARNING: Command file not found, cannot ping session")
|
|
135
142
|
except Exception as e:
|
|
136
|
-
print(
|
|
143
|
+
print("Error during ping test: {0}".format(e))
|
|
137
144
|
traceback.print_exc()
|
|
138
145
|
|
|
139
146
|
# Make final determination based on all checks
|
|
@@ -144,21 +151,21 @@ def check_session_status():
|
|
|
144
151
|
if session_state.get('project_open', False):
|
|
145
152
|
project_name = session_state.get('project_name', 'Unknown')
|
|
146
153
|
project_path = session_state.get('project_path', 'Unknown')
|
|
147
|
-
project_info =
|
|
154
|
+
project_info = ", Project: {0} ({1})".format(project_name, project_path)
|
|
148
155
|
|
|
149
156
|
# Include session info
|
|
150
157
|
session_id = session_state.get('session_id', 'Unknown')
|
|
151
158
|
start_time = session_state.get('start_time', 0)
|
|
152
159
|
uptime = time.time() - start_time if start_time else 0
|
|
153
|
-
uptime_str =
|
|
160
|
+
uptime_str = "{0:.0f} seconds".format(uptime) if uptime < 60 else "{0:.1f} minutes".format(uptime/60)
|
|
154
161
|
|
|
155
162
|
if session_active:
|
|
156
|
-
print(
|
|
163
|
+
print("{0}: Session is active (ID: {1}, Status: {2}, Uptime: {3}{4})".format(SCRIPT_SUCCESS_MARKER, session_id, status, uptime_str, project_info))
|
|
157
164
|
return True
|
|
158
165
|
else:
|
|
159
166
|
# If we get here, session may be in a state where files exist but process is dead
|
|
160
167
|
if active and status in ["running", "busy"] and lock_is_stale and not ping_success:
|
|
161
|
-
print(
|
|
168
|
+
print("{0}: Session appears to be zombie (ID: {1}, Status: {2} but unresponsive)".format(SCRIPT_SUCCESS_MARKER, session_id, status))
|
|
162
169
|
# Update session state to error so it will be considered inactive
|
|
163
170
|
try:
|
|
164
171
|
session_state['status'] = 'error'
|
|
@@ -170,14 +177,14 @@ def check_session_status():
|
|
|
170
177
|
|
|
171
178
|
print("Updated session state to error due to unresponsiveness")
|
|
172
179
|
except Exception as e:
|
|
173
|
-
print(
|
|
180
|
+
print("Error updating session state: {0}".format(e))
|
|
174
181
|
else:
|
|
175
|
-
print(
|
|
182
|
+
print("{0}: Session is not active (Status: {1})".format(SCRIPT_SUCCESS_MARKER, status))
|
|
176
183
|
|
|
177
184
|
return False
|
|
178
185
|
|
|
179
186
|
except Exception as e:
|
|
180
|
-
print(
|
|
187
|
+
print("{0}: Error checking session status - {1}".format(SCRIPT_ERROR_MARKER, str(e)))
|
|
181
188
|
traceback.print_exc()
|
|
182
189
|
return False
|
|
183
190
|
|
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
import sys, os,
|
|
2
|
-
|
|
1
|
+
import sys, os, time, tempfile, traceback
|
|
2
|
+
|
|
3
|
+
# Import JSON handling for IronPython 2.7 compatibility
|
|
4
|
+
try:
|
|
5
|
+
import json
|
|
6
|
+
except ImportError:
|
|
7
|
+
# For IronPython 2.7, we need to use simplejson instead
|
|
8
|
+
import simplejson as json
|
|
9
|
+
|
|
10
|
+
# Import scriptengine
|
|
11
|
+
try:
|
|
12
|
+
import scriptengine as script_engine
|
|
13
|
+
except ImportError:
|
|
14
|
+
# Log the error and continue - we'll handle this gracefully
|
|
15
|
+
print("Error importing scriptengine. This script must be run within CODESYS environment.")
|
|
16
|
+
script_engine = None
|
|
3
17
|
|
|
4
18
|
# Define markers for script results
|
|
5
19
|
SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
|
|
@@ -25,7 +39,7 @@ def setup_session_dir():
|
|
|
25
39
|
try:
|
|
26
40
|
with open(SESSION_STATE_FILE, 'r') as f:
|
|
27
41
|
old_state = json.load(f)
|
|
28
|
-
print(
|
|
42
|
+
print("Cleaning up previous session (ID: {0})".format(old_state.get('session_id', 'unknown')))
|
|
29
43
|
except:
|
|
30
44
|
pass
|
|
31
45
|
|
|
@@ -36,9 +50,9 @@ def setup_session_dir():
|
|
|
36
50
|
if os.path.isfile(filepath):
|
|
37
51
|
os.unlink(filepath)
|
|
38
52
|
except Exception as e:
|
|
39
|
-
print(
|
|
53
|
+
print("Error removing file {0}: {1}".format(filepath, e))
|
|
40
54
|
except Exception as e:
|
|
41
|
-
print(
|
|
55
|
+
print("Error cleaning up previous session: {0}".format(e))
|
|
42
56
|
|
|
43
57
|
# Create session directory
|
|
44
58
|
if not os.path.exists(SESSION_DIR):
|
|
@@ -74,12 +88,12 @@ def setup_session_dir():
|
|
|
74
88
|
with open(SESSION_LOCK_FILE, 'w') as f:
|
|
75
89
|
f.write(str(time.time()))
|
|
76
90
|
|
|
77
|
-
print(
|
|
78
|
-
print(
|
|
91
|
+
print("Session directory setup at: {0}".format(SESSION_DIR))
|
|
92
|
+
print("Session ID: {0}".format(session_id))
|
|
79
93
|
return True
|
|
80
94
|
except Exception as e:
|
|
81
|
-
print(
|
|
82
|
-
print(
|
|
95
|
+
print("Failed to set up session directory: {0}".format(e))
|
|
96
|
+
print("{0}: Session setup failed - {1}".format(SCRIPT_ERROR_MARKER, str(e)))
|
|
83
97
|
return False
|
|
84
98
|
|
|
85
99
|
def update_session_state(status="running"):
|
|
@@ -90,20 +104,29 @@ def update_session_state(status="running"):
|
|
|
90
104
|
project_name = "No project open"
|
|
91
105
|
project_path = "N/A"
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
if
|
|
95
|
-
|
|
107
|
+
# Check if script_engine was properly imported
|
|
108
|
+
if script_engine is None:
|
|
109
|
+
print("Warning: scriptengine not available, cannot get project info")
|
|
110
|
+
else:
|
|
111
|
+
# Try to get primary project
|
|
96
112
|
try:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
primary_project = script_engine.projects.primary
|
|
114
|
+
if primary_project:
|
|
115
|
+
project_open = True
|
|
116
|
+
try:
|
|
117
|
+
project_path = os.path.normcase(os.path.abspath(primary_project.path))
|
|
118
|
+
try:
|
|
119
|
+
project_name = primary_project.get_name()
|
|
120
|
+
if not project_name:
|
|
121
|
+
project_name = "Unnamed (path: {0})".format(os.path.basename(project_path))
|
|
122
|
+
except:
|
|
123
|
+
project_name = "Unnamed (path: {0})".format(os.path.basename(project_path))
|
|
124
|
+
except Exception as e_path:
|
|
125
|
+
project_path = "N/A (Error: {0})".format(str(e_path))
|
|
126
|
+
project_name = "Unnamed (Path Error)"
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print("Error accessing script_engine.projects: {0}".format(str(e)))
|
|
129
|
+
traceback.print_exc()
|
|
107
130
|
|
|
108
131
|
# Read current state file
|
|
109
132
|
try:
|
|
@@ -133,7 +156,7 @@ def update_session_state(status="running"):
|
|
|
133
156
|
|
|
134
157
|
return True
|
|
135
158
|
except Exception as e:
|
|
136
|
-
print(
|
|
159
|
+
print("Failed to update session state: {0}".format(e))
|
|
137
160
|
traceback.print_exc()
|
|
138
161
|
return False
|
|
139
162
|
|
|
@@ -189,13 +212,13 @@ def execute_command(command_data):
|
|
|
189
212
|
except Exception as e:
|
|
190
213
|
traceback.print_exc()
|
|
191
214
|
result["success"] = False
|
|
192
|
-
result["error"] =
|
|
193
|
-
result["output"] =
|
|
215
|
+
result["error"] = "Error executing script: {0}\n{1}".format(str(e), traceback.format_exc())
|
|
216
|
+
result["output"] = "{0}: {1}".format(SCRIPT_ERROR_MARKER, str(e))
|
|
194
217
|
|
|
195
218
|
elif cmd_type == "stop":
|
|
196
219
|
# Stop command received
|
|
197
220
|
result["success"] = True
|
|
198
|
-
result["output"] =
|
|
221
|
+
result["output"] = "{0}: Session stopping".format(SCRIPT_SUCCESS_MARKER)
|
|
199
222
|
|
|
200
223
|
# Update session state to mark as stopping
|
|
201
224
|
update_session_state("stopping")
|
|
@@ -206,12 +229,12 @@ def execute_command(command_data):
|
|
|
206
229
|
elif cmd_type == "ping":
|
|
207
230
|
# Simple ping command to check if session is alive
|
|
208
231
|
result["success"] = True
|
|
209
|
-
result["output"] =
|
|
232
|
+
result["output"] = "{0}: Session is active".format(SCRIPT_SUCCESS_MARKER)
|
|
210
233
|
|
|
211
234
|
else:
|
|
212
235
|
result["success"] = False
|
|
213
|
-
result["error"] =
|
|
214
|
-
result["output"] =
|
|
236
|
+
result["error"] = "Unknown command type: {0}".format(cmd_type)
|
|
237
|
+
result["output"] = "{0}: Unknown command type: {1}".format(SCRIPT_ERROR_MARKER, cmd_type)
|
|
215
238
|
|
|
216
239
|
# Update session state after command execution
|
|
217
240
|
update_session_state()
|
|
@@ -223,8 +246,8 @@ def execute_command(command_data):
|
|
|
223
246
|
return {
|
|
224
247
|
"id": command_data.get("id", "unknown"),
|
|
225
248
|
"success": False,
|
|
226
|
-
"output":
|
|
227
|
-
"error":
|
|
249
|
+
"output": "{0}: Command execution failed".format(SCRIPT_ERROR_MARKER),
|
|
250
|
+
"error": "{0}\n{1}".format(str(e), traceback.format_exc())
|
|
228
251
|
}, False
|
|
229
252
|
|
|
230
253
|
def check_command():
|
|
@@ -292,7 +315,7 @@ def check_command():
|
|
|
292
315
|
|
|
293
316
|
return False
|
|
294
317
|
except Exception as e:
|
|
295
|
-
print(
|
|
318
|
+
print("Error checking command: {0}".format(e))
|
|
296
319
|
traceback.print_exc()
|
|
297
320
|
return False
|
|
298
321
|
|
|
@@ -308,7 +331,7 @@ def update_last_activity():
|
|
|
308
331
|
with open(SESSION_STATE_FILE, 'w') as f:
|
|
309
332
|
json.dump(state, f, indent=2)
|
|
310
333
|
except Exception as e:
|
|
311
|
-
print(
|
|
334
|
+
print("Error updating last activity: {0}".format(e))
|
|
312
335
|
|
|
313
336
|
def check_inactivity_timeout():
|
|
314
337
|
"""Check if session has been inactive for too long"""
|
|
@@ -321,12 +344,12 @@ def check_inactivity_timeout():
|
|
|
321
344
|
current_time = time.time()
|
|
322
345
|
|
|
323
346
|
if (current_time - last_activity) > MAX_INACTIVITY_TIME:
|
|
324
|
-
print(
|
|
347
|
+
print("Session inactive for {0:.1f} seconds, shutting down".format(current_time - last_activity))
|
|
325
348
|
return True
|
|
326
349
|
|
|
327
350
|
return False
|
|
328
351
|
except Exception as e:
|
|
329
|
-
print(
|
|
352
|
+
print("Error checking inactivity timeout: {0}".format(e))
|
|
330
353
|
return False
|
|
331
354
|
|
|
332
355
|
def command_loop():
|
|
@@ -334,9 +357,9 @@ def command_loop():
|
|
|
334
357
|
stop_requested = False
|
|
335
358
|
last_state_update = time.time()
|
|
336
359
|
|
|
337
|
-
print(
|
|
338
|
-
print(
|
|
339
|
-
print(
|
|
360
|
+
print("Starting command loop...")
|
|
361
|
+
print("Command file: {0}".format(COMMAND_FILE))
|
|
362
|
+
print("Result file: {0}".format(RESULT_FILE))
|
|
340
363
|
|
|
341
364
|
while not stop_requested:
|
|
342
365
|
try:
|
|
@@ -363,7 +386,7 @@ def command_loop():
|
|
|
363
386
|
time.sleep(POLL_INTERVAL)
|
|
364
387
|
|
|
365
388
|
except Exception as e:
|
|
366
|
-
print(
|
|
389
|
+
print("Error in command loop: {0}".format(e))
|
|
367
390
|
traceback.print_exc()
|
|
368
391
|
# Sleep for a bit
|
|
369
392
|
time.sleep(POLL_INTERVAL)
|
|
@@ -381,17 +404,17 @@ def main():
|
|
|
381
404
|
update_session_state()
|
|
382
405
|
|
|
383
406
|
# Start command loop
|
|
384
|
-
print(
|
|
407
|
+
print("{0}: CODESYS session started".format(SCRIPT_SUCCESS_MARKER))
|
|
385
408
|
command_loop()
|
|
386
409
|
|
|
387
410
|
# Update final state
|
|
388
411
|
update_session_state("stopped")
|
|
389
412
|
|
|
390
|
-
print(
|
|
413
|
+
print("{0}: CODESYS session stopped cleanly".format(SCRIPT_SUCCESS_MARKER))
|
|
391
414
|
sys.exit(0)
|
|
392
415
|
except Exception as e:
|
|
393
|
-
print(
|
|
394
|
-
print(
|
|
416
|
+
print("Error in session: {0}".format(e))
|
|
417
|
+
print("{0}: Session failed - {1}".format(SCRIPT_ERROR_MARKER, str(e)))
|
|
395
418
|
traceback.print_exc()
|
|
396
419
|
sys.exit(1)
|
|
397
420
|
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import sys, os,
|
|
1
|
+
import sys, os, time, traceback
|
|
2
2
|
import tempfile
|
|
3
3
|
|
|
4
|
+
# Import JSON handling for IronPython 2.7 compatibility
|
|
5
|
+
try:
|
|
6
|
+
import json
|
|
7
|
+
except ImportError:
|
|
8
|
+
# For IronPython 2.7, we need to use simplejson instead
|
|
9
|
+
import simplejson as json
|
|
10
|
+
|
|
4
11
|
# Define markers for script results
|
|
5
12
|
SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
|
|
6
13
|
SCRIPT_ERROR_MARKER = "SCRIPT_ERROR"
|
|
@@ -17,12 +24,12 @@ def stop_session():
|
|
|
17
24
|
try:
|
|
18
25
|
# Check if session directory exists
|
|
19
26
|
if not os.path.exists(SESSION_DIR):
|
|
20
|
-
print(
|
|
27
|
+
print("{0}: No session directory found at {1}".format(SCRIPT_ERROR_MARKER, SESSION_DIR))
|
|
21
28
|
return False
|
|
22
29
|
|
|
23
30
|
# Check if session state file exists
|
|
24
31
|
if not os.path.exists(SESSION_STATE_FILE):
|
|
25
|
-
print(
|
|
32
|
+
print("{0}: No session state file found at {1}".format(SCRIPT_ERROR_MARKER, SESSION_STATE_FILE))
|
|
26
33
|
return False
|
|
27
34
|
|
|
28
35
|
# Read session state
|
|
@@ -30,17 +37,17 @@ def stop_session():
|
|
|
30
37
|
with open(SESSION_STATE_FILE, 'r') as f:
|
|
31
38
|
session_state = json.load(f)
|
|
32
39
|
except json.JSONDecodeError:
|
|
33
|
-
print(
|
|
40
|
+
print("{0}: Invalid session state file".format(SCRIPT_ERROR_MARKER))
|
|
34
41
|
return False
|
|
35
42
|
|
|
36
43
|
# Check if session is active
|
|
37
44
|
if not session_state.get('active', False) or session_state.get('status') == 'stopped':
|
|
38
|
-
print(
|
|
45
|
+
print("{0}: Session is already stopped".format(SCRIPT_SUCCESS_MARKER))
|
|
39
46
|
return True
|
|
40
47
|
|
|
41
48
|
# Check if command file exists
|
|
42
49
|
if not os.path.exists(COMMAND_FILE):
|
|
43
|
-
print(
|
|
50
|
+
print("{0}: Command file not found at {1}".format(SCRIPT_ERROR_MARKER, COMMAND_FILE))
|
|
44
51
|
return False
|
|
45
52
|
|
|
46
53
|
# Try to read the lock file to check if session is still alive
|
|
@@ -52,13 +59,13 @@ def stop_session():
|
|
|
52
59
|
|
|
53
60
|
current_time = time.time()
|
|
54
61
|
if (current_time - lock_time) > 30:
|
|
55
|
-
print(
|
|
62
|
+
print("WARNING: Session lock file is stale ({0:.1f} seconds old). Session may have crashed.".format(current_time - lock_time))
|
|
56
63
|
except Exception as e:
|
|
57
|
-
print(
|
|
64
|
+
print("Warning: Error reading lock file: {0}".format(e))
|
|
58
65
|
|
|
59
66
|
# Create stop command
|
|
60
67
|
stop_command = {
|
|
61
|
-
"id":
|
|
68
|
+
"id": "stop_{0}".format(int(time.time())),
|
|
62
69
|
"type": "stop",
|
|
63
70
|
"timestamp": time.time()
|
|
64
71
|
}
|
|
@@ -73,7 +80,7 @@ def stop_session():
|
|
|
73
80
|
with open(COMMAND_FILE, 'w') as f:
|
|
74
81
|
json.dump(command_data, f, indent=2)
|
|
75
82
|
|
|
76
|
-
print(
|
|
83
|
+
print("Stop command sent to session")
|
|
77
84
|
|
|
78
85
|
# Wait for result (with timeout)
|
|
79
86
|
timeout = 15 # seconds (increased timeout for more reliability)
|
|
@@ -94,10 +101,10 @@ def stop_session():
|
|
|
94
101
|
result = result_data.get("result")
|
|
95
102
|
if result.get('id') == stop_command['id']:
|
|
96
103
|
result_received = True
|
|
97
|
-
print(
|
|
104
|
+
print("Session stop result: {0}".format(result.get('output', 'No output')))
|
|
98
105
|
break
|
|
99
106
|
except Exception as e:
|
|
100
|
-
print(
|
|
107
|
+
print("Error reading result: {0}".format(e))
|
|
101
108
|
time.sleep(0.5)
|
|
102
109
|
|
|
103
110
|
# Update session state to indicate it's stopped regardless of result
|
|
@@ -112,15 +119,15 @@ def stop_session():
|
|
|
112
119
|
with open(SESSION_STATE_FILE, 'w') as f:
|
|
113
120
|
json.dump(session_state, f, indent=2)
|
|
114
121
|
except Exception as e:
|
|
115
|
-
print(
|
|
122
|
+
print("Warning: Failed to update session state file: {0}".format(e))
|
|
116
123
|
|
|
117
124
|
if result_received:
|
|
118
|
-
print(
|
|
125
|
+
print("{0}: Session stopped successfully".format(SCRIPT_SUCCESS_MARKER))
|
|
119
126
|
return True
|
|
120
127
|
else:
|
|
121
128
|
# If we didn't get a response, the session may be hung
|
|
122
129
|
# Force a cleanup by removing session files
|
|
123
|
-
print(
|
|
130
|
+
print("Warning: Session did not respond to stop command. Forcing cleanup.")
|
|
124
131
|
try:
|
|
125
132
|
# Try to remove all session files to ensure a clean state for next session
|
|
126
133
|
for filename in os.listdir(SESSION_DIR):
|
|
@@ -129,16 +136,16 @@ def stop_session():
|
|
|
129
136
|
if os.path.isfile(filepath):
|
|
130
137
|
os.unlink(filepath)
|
|
131
138
|
except Exception as e:
|
|
132
|
-
print(
|
|
139
|
+
print("Error removing file {0}: {1}".format(filepath, e))
|
|
133
140
|
|
|
134
|
-
print(
|
|
141
|
+
print("{0}: Session files cleaned up forcefully".format(SCRIPT_SUCCESS_MARKER))
|
|
135
142
|
return True
|
|
136
143
|
except Exception as e:
|
|
137
|
-
print(
|
|
144
|
+
print("{0}: Failed to force cleanup - {1}".format(SCRIPT_ERROR_MARKER, str(e)))
|
|
138
145
|
return False
|
|
139
146
|
|
|
140
147
|
except Exception as e:
|
|
141
|
-
print(
|
|
148
|
+
print("{0}: Error stopping session - {1}".format(SCRIPT_ERROR_MARKER, str(e)))
|
|
142
149
|
traceback.print_exc()
|
|
143
150
|
return False
|
|
144
151
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
f"{SCRIPT_ERROR_MARKER}: Invalid session state file" | "{0}: Invalid session state file".format(SCRIPT_ERROR_MARKER)
|
|
2
|
+
f"{SCRIPT_SUCCESS_MARKER}: Session is not active (Status: {status})" | "{0}: Session is not active (Status: {1})".format(SCRIPT_SUCCESS_MARKER, status)
|
|
3
|
+
f"WARNING: Session lock file is stale by {time_diff:.1f} seconds" | "WARNING: Session lock file is stale by {0:.1f} seconds".format(time_diff)
|
|
4
|
+
f"Error reading lock file: {e}" | "Error reading result: {0}".format(e)
|
|
5
|
+
f"ping_{int(time.time())}" | "ping_{0}".format(int(time.time()))
|
|
6
|
+
f"WARNING: Session did not respond to ping within {timeout} seconds" | "WARNING: Session did not respond to ping within {0} seconds".format(timeout)
|
|
7
|
+
f"Error resetting command file: {e}" | "Error resetting command file: {0}".format(e)
|
|
8
|
+
f"Error during ping test: {e}" | "Error during ping test: {0}".format(e)
|
|
9
|
+
f", Project: {project_name} ({project_path})" | ", Project: {0} ({1})".format(project_name, project_path)
|
|
10
|
+
f"{uptime:.0f} seconds" | "{0:.0f} seconds".format(uptime)
|
|
11
|
+
f"{uptime/60:.1f} minutes" | "{0:.1f} minutes".format(uptime/60)
|
|
12
|
+
f"{SCRIPT_SUCCESS_MARKER}: Session is active (ID: {session_id}, Status: {status}, Uptime: {uptime_str}{project_info})" | "{0}: Session is active (ID: {1}, Status: {2}, Uptime: {3}{4})".format(SCRIPT_SUCCESS_MARKER, session_id, status, uptime_str, project_info)
|
|
13
|
+
f"{SCRIPT_SUCCESS_MARKER}: Session appears to be zombie (ID: {session_id}, Status: {status} but unresponsive)" | "{0}: Session appears to be zombie (ID: {1}, Status: {2} but unresponsive)".format(SCRIPT_SUCCESS_MARKER, session_id, status)
|
|
14
|
+
f"Error updating session state: {e}" | "Error updating session state: {0}".format(e)
|
|
15
|
+
f"{SCRIPT_SUCCESS_MARKER}: Session is not active (Status: {status})" | "{0}: Session is not active (Status: {1})".format(SCRIPT_SUCCESS_MARKER, status)
|
|
16
|
+
f"{SCRIPT_ERROR_MARKER}: Error checking session status - {str(e)}" | "{0}: Error checking session status - {1}".format(SCRIPT_ERROR_MARKER, str(e))
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Script to fix f-strings in Python files to make them compatible with IronPython 2.7.
|
|
4
|
+
Converts f-strings to str.format() calls.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
def process_file(filepath):
|
|
12
|
+
print(f"Processing {filepath}")
|
|
13
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
14
|
+
content = f.read()
|
|
15
|
+
|
|
16
|
+
# Find and replace f-strings
|
|
17
|
+
# This is a simplified approach - complex nested expressions might need manual review
|
|
18
|
+
pattern = r'f"([^"]*\{[^}]+\}[^"]*)"'
|
|
19
|
+
|
|
20
|
+
# Count potential matches
|
|
21
|
+
matches = re.findall(pattern, content)
|
|
22
|
+
print(f"Found {len(matches)} potential f-strings")
|
|
23
|
+
|
|
24
|
+
# Function to convert each match
|
|
25
|
+
def replace_fstring(match):
|
|
26
|
+
template_str = match.group(1)
|
|
27
|
+
# Replace {var} with {0}, {1}, etc.
|
|
28
|
+
var_pattern = r'\{([^}:]+)(?::([^}]+))?\}'
|
|
29
|
+
var_matches = re.finditer(var_pattern, template_str)
|
|
30
|
+
|
|
31
|
+
format_args = []
|
|
32
|
+
i = 0
|
|
33
|
+
|
|
34
|
+
new_template = template_str
|
|
35
|
+
for m in var_matches:
|
|
36
|
+
var_name = m.group(1)
|
|
37
|
+
format_spec = m.group(2)
|
|
38
|
+
|
|
39
|
+
if format_spec:
|
|
40
|
+
repl = "{" + str(i) + ":" + format_spec + "}"
|
|
41
|
+
else:
|
|
42
|
+
repl = "{" + str(i) + "}"
|
|
43
|
+
|
|
44
|
+
# Store the argument
|
|
45
|
+
format_args.append(var_name)
|
|
46
|
+
|
|
47
|
+
# Replace in the template
|
|
48
|
+
new_template = new_template.replace(m.group(0), repl, 1)
|
|
49
|
+
i += 1
|
|
50
|
+
|
|
51
|
+
# Build the complete format expression
|
|
52
|
+
args_str = ", ".join(format_args)
|
|
53
|
+
return f'"{new_template}".format({args_str})'
|
|
54
|
+
|
|
55
|
+
# Apply the replacements
|
|
56
|
+
new_content = re.sub(pattern, replace_fstring, content)
|
|
57
|
+
|
|
58
|
+
# Write the changes back
|
|
59
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
60
|
+
f.write(new_content)
|
|
61
|
+
|
|
62
|
+
print(f"Completed processing {filepath}")
|
|
63
|
+
|
|
64
|
+
def main():
|
|
65
|
+
if len(sys.argv) < 2:
|
|
66
|
+
print("Usage: python fix_fstrings.py <file_paths...>")
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
|
|
69
|
+
for file_path in sys.argv[1:]:
|
|
70
|
+
if os.path.exists(file_path):
|
|
71
|
+
process_file(file_path)
|
|
72
|
+
else:
|
|
73
|
+
print(f"Error: File {file_path} not found")
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
main()
|