@codesys/mcp-toolkit 1.5.8 → 2.0.0-alpha.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.
Files changed (110) hide show
  1. package/CHANGELOG.md +153 -189
  2. package/LICENSE +21 -21
  3. package/README.md +224 -107
  4. package/dist/actions.d.ts +28 -0
  5. package/dist/actions.js +98 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/bridge/bridge.d.ts +105 -0
  8. package/dist/bridge/bridge.js +400 -0
  9. package/dist/bridge/bridge.js.map +1 -0
  10. package/dist/cli.d.ts +2 -0
  11. package/dist/cli.js +196 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/codesys/cache.d.ts +48 -0
  14. package/dist/codesys/cache.js +82 -0
  15. package/dist/codesys/cache.js.map +1 -0
  16. package/dist/codesys/changes.d.ts +57 -0
  17. package/dist/codesys/changes.js +133 -0
  18. package/dist/codesys/changes.js.map +1 -0
  19. package/dist/codesys/decl.d.ts +45 -0
  20. package/dist/codesys/decl.js +251 -0
  21. package/dist/codesys/decl.js.map +1 -0
  22. package/dist/codesys/devices.d.ts +34 -0
  23. package/dist/codesys/devices.js +82 -0
  24. package/dist/codesys/devices.js.map +1 -0
  25. package/dist/codesys/diff.d.ts +13 -0
  26. package/dist/codesys/diff.js +61 -0
  27. package/dist/codesys/diff.js.map +1 -0
  28. package/dist/codesys/install.d.ts +40 -0
  29. package/dist/codesys/install.js +144 -0
  30. package/dist/codesys/install.js.map +1 -0
  31. package/dist/codesys/library-browse.d.ts +19 -0
  32. package/dist/codesys/library-browse.js +57 -0
  33. package/dist/codesys/library-browse.js.map +1 -0
  34. package/dist/codesys/outline.d.ts +32 -0
  35. package/dist/codesys/outline.js +219 -0
  36. package/dist/codesys/outline.js.map +1 -0
  37. package/dist/codesys/simulation.d.ts +34 -0
  38. package/dist/codesys/simulation.js +57 -0
  39. package/dist/codesys/simulation.js.map +1 -0
  40. package/dist/codesys/st.d.ts +36 -0
  41. package/dist/codesys/st.js +69 -0
  42. package/dist/codesys/st.js.map +1 -0
  43. package/dist/daemon.d.ts +46 -0
  44. package/dist/daemon.js +173 -0
  45. package/dist/daemon.js.map +1 -0
  46. package/dist/driver.d.ts +25 -0
  47. package/dist/driver.js +33 -0
  48. package/dist/driver.js.map +1 -0
  49. package/dist/panel/sim-panel.html +357 -0
  50. package/dist/panel/viewer.html +126 -0
  51. package/dist/panel-types.d.ts +20 -0
  52. package/dist/panel-types.js +3 -0
  53. package/dist/panel-types.js.map +1 -0
  54. package/dist/panel.d.ts +27 -0
  55. package/dist/panel.js +208 -0
  56. package/dist/panel.js.map +1 -0
  57. package/dist/server.d.ts +5 -0
  58. package/dist/server.js +33 -974
  59. package/dist/server.js.map +1 -0
  60. package/dist/tools.d.ts +103 -0
  61. package/dist/tools.js +793 -0
  62. package/dist/tools.js.map +1 -0
  63. package/dist/viewer.d.ts +14 -0
  64. package/dist/viewer.js +125 -0
  65. package/dist/viewer.js.map +1 -0
  66. package/package.json +37 -21
  67. package/watcher/mcp_compat.py +147 -0
  68. package/watcher/mcp_ops.py +1178 -0
  69. package/watcher/mcp_sim.py +246 -0
  70. package/watcher/watcher.py +255 -0
  71. package/.claude/settings.local.json +0 -29
  72. package/CLAUDE.md +0 -26
  73. package/CONTRIBUTING.md +0 -72
  74. package/README.md.enhanced +0 -155
  75. package/codesys-mcp-toolkit-1.2.0.tgz +0 -0
  76. package/codesys-mcp-toolkit-1.2.1.tgz +0 -0
  77. package/codesys-mcp-toolkit-1.5.7.tgz +0 -0
  78. package/codesys-mcp-toolkit-1.5.8.tgz +0 -0
  79. package/dist/bin.js +0 -75
  80. package/dist/check_session_paths.js +0 -149
  81. package/dist/codesys_interop.js +0 -435
  82. package/dist/direct_session_command.js +0 -293
  83. package/dist/scriptLoader.js +0 -165
  84. package/dist/scripts/check_status.py +0 -21
  85. package/dist/scripts/compile_project.py +0 -65
  86. package/dist/scripts/create_method.py +0 -80
  87. package/dist/scripts/create_pou.py +0 -102
  88. package/dist/scripts/create_project.py +0 -116
  89. package/dist/scripts/create_property.py +0 -78
  90. package/dist/scripts/ensure_project_open.py +0 -154
  91. package/dist/scripts/find_object_by_path.py +0 -111
  92. package/dist/scripts/get_pou_code.py +0 -91
  93. package/dist/scripts/get_project_structure.py +0 -72
  94. package/dist/scripts/open_project.py +0 -25
  95. package/dist/scripts/save_project.py +0 -29
  96. package/dist/scripts/session_status.py +0 -281
  97. package/dist/scripts/set_pou_code.py +0 -109
  98. package/dist/scripts/start_session.py +0 -809
  99. package/dist/scripts/stop_session.py +0 -324
  100. package/dist/sessionManager.js +0 -798
  101. package/docs/configuration.md +0 -141
  102. package/docs/index.md +0 -26
  103. package/docs/installation.md +0 -84
  104. package/docs/session_management.md +0 -110
  105. package/examples/README.md +0 -78
  106. package/examples/sample_config.json +0 -32
  107. package/replacements_session_status.txt +0 -16
  108. package/scriptDoc.txt +0 -8679
  109. package/scripts/build.js +0 -25
  110. package/scripts/fix_fstrings.py +0 -76
@@ -1,324 +0,0 @@
1
- import sys, os, time, traceback
2
- import tempfile
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
- try:
10
- import simplejson as json
11
- except ImportError:
12
- print("Neither json nor simplejson could be imported. Check IronPython installation.")
13
- # Provide a minimal fallback implementation for basic needs
14
- class DummyJson:
15
- @staticmethod
16
- def load(file_obj):
17
- content = file_obj.read()
18
- # Very basic parsing for our limited needs
19
- if content.strip().startswith("{") and content.strip().endswith("}"):
20
- result = {}
21
- parts = content.strip()[1:-1].split(",")
22
- for part in parts:
23
- if ":" in part:
24
- k, v = part.split(":", 1)
25
- k = k.strip().strip('"\'')
26
- v = v.strip().strip('"\'')
27
- result[k] = v
28
- return result
29
- return {}
30
-
31
- @staticmethod
32
- def loads(s):
33
- if s.strip().startswith("{") and s.strip().endswith("}"):
34
- result = {}
35
- parts = s.strip()[1:-1].split(",")
36
- for part in parts:
37
- if ":" in part:
38
- k, v = part.split(":", 1)
39
- k = k.strip().strip('"\'')
40
- v = v.strip().strip('"\'')
41
- result[k] = v
42
- return result
43
- return {}
44
-
45
- @staticmethod
46
- def dump(obj, file_obj, indent=None):
47
- if isinstance(obj, dict):
48
- pairs = []
49
- for k, v in obj.items():
50
- if isinstance(v, str):
51
- pairs.append('"{0}": "{1}"'.format(k, v))
52
- else:
53
- pairs.append('"{0}": {1}'.format(k, v))
54
- file_obj.write("{" + ", ".join(pairs) + "}")
55
- else:
56
- file_obj.write(str(obj))
57
-
58
- json = DummyJson()
59
-
60
- # Print Python and platform info
61
- print("Python version: {0}.{1}.{2}".format(sys.version_info[0], sys.version_info[1], sys.version_info[2]))
62
- print("Platform: {0}".format(sys.platform))
63
- print("Executable: {0}".format(sys.executable))
64
-
65
- # Define markers for script results
66
- SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
67
- SCRIPT_ERROR_MARKER = "SCRIPT_ERROR"
68
-
69
- # Session state and command files
70
- SESSION_DIR = os.path.join(tempfile.gettempdir(), "codesys_session")
71
- SESSION_STATE_FILE = os.path.join(SESSION_DIR, "session_state.json")
72
- COMMAND_FILE = os.path.join(SESSION_DIR, "command.json")
73
- RESULT_FILE = os.path.join(SESSION_DIR, "result.json")
74
- SESSION_LOCK_FILE = os.path.join(SESSION_DIR, "session.lock")
75
-
76
- # Print key information at start
77
- print("Session stop script starting")
78
- print("tempdir: {0}".format(tempfile.gettempdir()))
79
- print("SESSION_DIR: {0}".format(SESSION_DIR))
80
- print("SESSION_STATE_FILE: {0}".format(SESSION_STATE_FILE))
81
- print("COMMAND_FILE: {0}".format(COMMAND_FILE))
82
- print("RESULT_FILE: {0}".format(RESULT_FILE))
83
- print("SESSION_LOCK_FILE: {0}".format(SESSION_LOCK_FILE))
84
-
85
- def stop_session():
86
- """Stop a running CODESYS session"""
87
- try:
88
- print("Attempting to stop CODESYS session")
89
-
90
- # Check if session directory exists
91
- if not os.path.exists(SESSION_DIR):
92
- print("{0}: No session directory found at {1}".format(SCRIPT_SUCCESS_MARKER, SESSION_DIR))
93
- # Consider this a success since there's no session to stop
94
- return True
95
-
96
- # Check if session state file exists
97
- if not os.path.exists(SESSION_STATE_FILE):
98
- print("{0}: No session state file found at {1}".format(SCRIPT_SUCCESS_MARKER, SESSION_STATE_FILE))
99
- # No state file means no active session - clear any other potential session files
100
- try:
101
- for filename in os.listdir(SESSION_DIR):
102
- filepath = os.path.join(SESSION_DIR, filename)
103
- try:
104
- if os.path.isfile(filepath):
105
- os.unlink(filepath)
106
- print("Removed orphaned session file: {0}".format(filepath))
107
- except Exception as e:
108
- print("Error removing file {0}: {1}".format(filepath, e))
109
- except Exception as e:
110
- print("Error cleaning session directory: {0}".format(e))
111
-
112
- return True
113
-
114
- # Read session state
115
- try:
116
- with open(SESSION_STATE_FILE, 'r') as f:
117
- session_state = json.load(f)
118
- except Exception as e:
119
- print("Error reading session state file: {0}".format(e))
120
- # Force cleanup regardless of the error
121
- try:
122
- for filename in os.listdir(SESSION_DIR):
123
- filepath = os.path.join(SESSION_DIR, filename)
124
- try:
125
- if os.path.isfile(filepath):
126
- os.unlink(filepath)
127
- print("Removed session file due to corrupt state: {0}".format(filepath))
128
- except Exception as rm_err:
129
- print("Error removing file {0}: {1}".format(filepath, rm_err))
130
- except Exception as dir_err:
131
- print("Error cleaning session directory: {0}".format(dir_err))
132
-
133
- print("{0}: Session files cleaned up forcefully due to corrupt state".format(SCRIPT_SUCCESS_MARKER))
134
- return True
135
-
136
- # Check if session is already marked as inactive
137
- if not session_state.get('active', False) or session_state.get('status') in ['stopped', 'error', 'timeout']:
138
- print("{0}: Session is already marked as {1}".format(
139
- SCRIPT_SUCCESS_MARKER, session_state.get('status', 'inactive')))
140
-
141
- # Remove session files to ensure a clean state
142
- try:
143
- for filename in os.listdir(SESSION_DIR):
144
- filepath = os.path.join(SESSION_DIR, filename)
145
- try:
146
- if os.path.isfile(filepath):
147
- os.unlink(filepath)
148
- print("Removed session file for cleanup: {0}".format(filepath))
149
- except Exception as e:
150
- print("Error removing file {0}: {1}".format(filepath, e))
151
- except Exception as e:
152
- print("Error cleaning session directory: {0}".format(e))
153
-
154
- return True
155
-
156
- # Get lock file age to check if session is responsive
157
- lock_is_stale = False
158
- current_time = time.time()
159
-
160
- try:
161
- if os.path.exists(SESSION_LOCK_FILE):
162
- with open(SESSION_LOCK_FILE, 'r') as f:
163
- lock_time = float(f.read().strip())
164
- lock_age = current_time - lock_time
165
-
166
- if lock_age > 30:
167
- print("WARNING: Session lock file is stale ({0:.1f} seconds old). Session may have crashed.".format(lock_age))
168
- lock_is_stale = True
169
- else:
170
- print("WARNING: Session lock file not found. Session may have crashed.")
171
- lock_is_stale = True
172
- except Exception as e:
173
- print("Warning: Error reading lock file: {0}".format(e))
174
- lock_is_stale = True
175
-
176
- # If the command file doesn't exist, we have a zombie session
177
- if not os.path.exists(COMMAND_FILE):
178
- print("WARNING: Command file not found. Session is in a zombie state.")
179
- lock_is_stale = True
180
-
181
- # If the session appears to be a zombie (stale lock), don't try to send a command
182
- # Just force cleanup immediately
183
- if lock_is_stale:
184
- print("Session appears to be unresponsive. Skipping command and forcing cleanup.")
185
- try:
186
- # Update state file first
187
- try:
188
- session_state['active'] = False
189
- session_state['status'] = "error"
190
- session_state['end_time'] = current_time
191
- session_state['error_reason'] = "Unresponsive session, forced cleanup"
192
-
193
- with open(SESSION_STATE_FILE, 'w') as f:
194
- json.dump(session_state, f, indent=2)
195
- print("Updated session state to error before cleanup")
196
- except Exception as state_err:
197
- print("Warning: Failed to update session state file: {0}".format(state_err))
198
-
199
- # Remove all session files
200
- for filename in os.listdir(SESSION_DIR):
201
- filepath = os.path.join(SESSION_DIR, filename)
202
- try:
203
- if os.path.isfile(filepath):
204
- os.unlink(filepath)
205
- print("Removed zombie session file: {0}".format(filepath))
206
- except Exception as e:
207
- print("Error removing file {0}: {1}".format(filepath, e))
208
-
209
- print("{0}: Zombie session files cleaned up forcefully".format(SCRIPT_SUCCESS_MARKER))
210
- return True
211
- except Exception as e:
212
- print("{0}: Failed to force cleanup - {1}".format(SCRIPT_ERROR_MARKER, str(e)))
213
- return False
214
-
215
- # For responsive sessions, try to send a stop command first
216
- try:
217
- # Create stop command
218
- stop_command = {
219
- "id": "stop_{0}".format(int(time.time())),
220
- "type": "stop",
221
- "timestamp": current_time
222
- }
223
-
224
- # Write command to file with pretty-printing
225
- command_data = {
226
- "status": "pending",
227
- "command": stop_command,
228
- "timestamp": current_time
229
- }
230
-
231
- print("Writing stop command to command file: {0}".format(COMMAND_FILE))
232
- with open(COMMAND_FILE, 'w') as f:
233
- json.dump(command_data, f, indent=2)
234
-
235
- print("Stop command sent to session")
236
- except Exception as cmd_err:
237
- print("Error sending stop command: {0}".format(cmd_err))
238
- # Continue to forced cleanup
239
-
240
- # Wait for result (with timeout)
241
- timeout = 15 # seconds (increased timeout for more reliability)
242
- start_time = time.time()
243
- result_received = False
244
-
245
- while not result_received and (time.time() - start_time) < timeout:
246
- # Check if result file exists
247
- if not os.path.exists(RESULT_FILE):
248
- time.sleep(0.5)
249
- continue
250
-
251
- try:
252
- with open(RESULT_FILE, 'r') as f:
253
- result_data = json.load(f)
254
-
255
- if result_data.get("status") == "complete" and result_data.get("result"):
256
- result = result_data.get("result")
257
- if result.get('id') == stop_command['id']:
258
- result_received = True
259
- print("Session stop result: {0}".format(result.get('output', 'No output')))
260
- break
261
- except Exception as e:
262
- print("Error reading result: {0}".format(e))
263
- time.sleep(0.5)
264
-
265
- # Update session state to indicate it's stopped regardless of result
266
- try:
267
- with open(SESSION_STATE_FILE, 'r') as f:
268
- session_state = json.load(f)
269
-
270
- session_state['active'] = False
271
- session_state['status'] = "stopped"
272
- session_state['end_time'] = time.time()
273
-
274
- with open(SESSION_STATE_FILE, 'w') as f:
275
- json.dump(session_state, f, indent=2)
276
- print("Updated session state to stopped")
277
- except Exception as e:
278
- print("Warning: Failed to update session state file: {0}".format(e))
279
-
280
- # Remove all session files for a clean state
281
- print("Cleaning up all session files")
282
- try:
283
- for filename in os.listdir(SESSION_DIR):
284
- filepath = os.path.join(SESSION_DIR, filename)
285
- try:
286
- if os.path.isfile(filepath):
287
- os.unlink(filepath)
288
- print("Removed session file: {0}".format(filepath))
289
- except Exception as e:
290
- print("Error removing file {0}: {1}".format(filepath, e))
291
-
292
- print("{0}: Session stopped and files cleaned up successfully".format(SCRIPT_SUCCESS_MARKER))
293
- return True
294
- except Exception as e:
295
- print("{0}: Error during final cleanup - {1}".format(SCRIPT_ERROR_MARKER, str(e)))
296
- return False
297
-
298
- except Exception as e:
299
- print("{0}: Error stopping session - {1}".format(SCRIPT_ERROR_MARKER, str(e)))
300
- traceback.print_exc()
301
-
302
- # Try one final desperate cleanup even if everything else failed
303
- try:
304
- if os.path.exists(SESSION_DIR):
305
- for filename in os.listdir(SESSION_DIR):
306
- filepath = os.path.join(SESSION_DIR, filename)
307
- try:
308
- if os.path.isfile(filepath):
309
- os.unlink(filepath)
310
- print("Emergency cleanup: Removed session file: {0}".format(filepath))
311
- except Exception:
312
- pass
313
- print("Emergency session cleanup attempted after error")
314
- except Exception:
315
- pass
316
-
317
- return False
318
-
319
- # Main script
320
- if __name__ == "__main__":
321
- if stop_session():
322
- sys.exit(0)
323
- else:
324
- sys.exit(1)