computer-use-ootb-internal 0.0.120__py3-none-any.whl → 0.0.122__py3-none-any.whl

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.
@@ -87,150 +87,20 @@ def receive_command():
87
87
  return jsonify({"message": f"Command {command_id} received and queued"}), 202 # Accepted
88
88
 
89
89
  # --- Helper Functions --- Only logging helpers needed adjustments
90
- def get_python_executable():
91
- python_exe = sys.executable
92
- if " " in python_exe and not python_exe.startswith('"'):
93
- python_exe = f'"{python_exe}"'
94
- return python_exe
95
-
96
- def get_pip_executable():
97
- """Tries to locate the pip executable in the same environment."""
98
- try:
99
- current_python = sys.executable
100
- log_info(f"get_pip_executable: sys.executable = {current_python}")
101
- python_path = pathlib.Path(current_python)
102
- # Common location is ../Scripts/pip.exe relative to python.exe
103
- pip_path = python_path.parent / "Scripts" / "pip.exe"
104
- log_info(f"get_pip_executable: Checking for pip at {pip_path}")
105
-
106
- if pip_path.exists():
107
- log_info(f"get_pip_executable: pip.exe found at {pip_path}")
108
- # Quote if necessary
109
- pip_exe = str(pip_path)
110
- if " " in pip_exe and not pip_exe.startswith('"'):
111
- pip_exe = f'"{pip_exe}"'
112
- return pip_exe
113
- else:
114
- log_error(f"get_pip_executable: pip.exe NOT found at {pip_path}. Falling back to 'python -m pip'.")
115
- # Fallback is intended here
116
- pass # Explicitly pass to reach the fallback return outside the else
117
-
118
- except Exception as e:
119
- log_error(f"get_pip_executable: Error determining pip path: {e}", exc_info=True)
120
- log_error("get_pip_executable: Falling back to 'python -m pip' due to error.")
121
-
122
- # Fallback return statement if 'exists' is false or an exception occurred
123
- return f"{get_python_executable()} -m pip"
124
-
125
- def log_info(msg):
126
- thread_name = threading.current_thread().name
127
- full_msg = f"[{thread_name}] {msg}"
128
- logging.info(full_msg)
129
- try:
130
- # Only log to event log from main service thread or known non-daemon threads if possible
131
- # Trying from waitress/flask threads might cause issues.
132
- # For simplicity, maybe remove event log integration or make it conditional.
133
- if threading.current_thread().name in ["MainThread", "CommandProcessor"]: # Example condition
134
- servicemanager.LogInfoMsg(str(full_msg))
135
- except Exception as e:
136
- logging.warning(f"Could not write info to Windows Event Log: {e}")
137
-
138
- def log_error(msg, exc_info=False):
139
- thread_name = threading.current_thread().name
140
- full_msg = f"[{thread_name}] {msg}"
141
- logging.error(full_msg, exc_info=exc_info)
142
- try:
143
- if threading.current_thread().name in ["MainThread", "CommandProcessor"]:
144
- servicemanager.LogErrorMsg(str(full_msg))
145
- except Exception as e:
146
- logging.warning(f"Could not write error to Windows Event Log: {e}")
147
-
148
- # --- PowerShell Task Scheduler Helpers ---
149
-
150
- _TASK_NAME_PREFIX = "OOTB_UserLogon_"
151
-
152
- def run_powershell_command(command, log_output=True):
153
- """Executes a PowerShell command and handles output/errors. Returns True on success."""
154
- # Use log_info from the service instance if available, otherwise use root logger
155
- logger = _service_instance.log_info if _service_instance else logging.info
156
- error_logger = _service_instance.log_error if _service_instance else logging.error
157
- logger(f"Executing PowerShell: {command}")
158
- try:
159
- # Using encoding important for non-ASCII usernames/paths
160
- result = subprocess.run(
161
- ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", command],
162
- capture_output=True, text=True, check=True, encoding='utf-8', errors='ignore'
163
- )
164
- if log_output and result.stdout:
165
- logger(f"PowerShell STDOUT:\n{result.stdout.strip()}")
166
- if log_output and result.stderr:
167
- logger(f"PowerShell STDERR:\n{result.stderr.strip()}") # Log stderr as info
168
- return True
169
- except FileNotFoundError:
170
- error_logger("'powershell.exe' not found. Cannot manage scheduled tasks.")
171
- return False
172
- except subprocess.CalledProcessError as e:
173
- error_logger(f"PowerShell command failed (Exit Code {e.returncode}):")
174
- error_logger(f" Command: {e.cmd}")
175
- if e.stdout: error_logger(f" STDOUT: {e.stdout.strip()}")
176
- if e.stderr: error_logger(f" STDERR: {e.stderr.strip()}")
177
- return False
178
- except Exception as e:
179
- error_logger(f"Unexpected error running PowerShell: {e}", exc_info=True)
180
- return False
181
-
182
- def create_or_update_logon_task(username, task_command, python_executable):
183
- """Creates or updates a scheduled task to run a command at user logon."""
184
- logger = _service_instance.log_info if _service_instance else logging.info
185
- error_logger = _service_instance.log_error if _service_instance else logging.error
186
- task_name = f"{_TASK_NAME_PREFIX}{username}"
187
- # Escape single quotes in paths and commands for PowerShell
188
- safe_python_exe = python_executable.replace("'", "''")
189
- # Ensure task_command is just the arguments, not the python exe itself
190
- command_parts = task_command.split(' ', 1)
191
- if len(command_parts) > 1 and command_parts[0] == python_executable:
192
- safe_task_command_args = command_parts[1].replace("'", "''")
193
- else: # Fallback if task_command doesn't start with python_exe
194
- safe_task_command_args = task_command.replace(python_executable, "").strip().replace("'", "''")
195
-
196
- safe_task_name = task_name.replace("'", "''")
197
- safe_username = username.replace("'", "''") # Handle usernames with quotes?
198
-
199
- action = f"$Action = New-ScheduledTaskAction -Execute '{safe_python_exe}' -Argument '{safe_task_command_args}'"
200
- trigger = f"$Trigger = New-ScheduledTaskTrigger -AtLogOn -User '{safe_username}'"
201
- principal = f"$Principal = New-ScheduledTaskPrincipal -UserId '{safe_username}' -LogonType Interactive"
202
- settings = "$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -ExecutionTimeLimit ([System.TimeSpan]::Zero) -RunOnlyIfNetworkAvailable:$false"
203
-
204
- command = f"""
205
- try {{
206
- {action}
207
- {trigger}
208
- {principal}
209
- {settings}
210
- Register-ScheduledTask -TaskName '{safe_task_name}' -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings -Force -ErrorAction Stop
211
- Write-Host "Scheduled task '{safe_task_name}' registered/updated successfully."
212
- }} catch {{
213
- Write-Error "Failed to register/update scheduled task '{safe_task_name}': $_"
214
- exit 1 # Indicate failure
215
- }}
216
- """
217
- success = run_powershell_command(command)
218
- if success:
219
- logger(f"Successfully created/updated scheduled task '{task_name}' for user '{username}'.")
220
- else:
221
- error_logger(f"Failed to create/update scheduled task '{task_name}' for user '{username}'.")
222
- return success
90
+ # Move these inside the class later
91
+ # def get_python_executable(): ...
92
+ # def get_pip_executable(): ...
93
+
94
+ # Define loggers at module level for use before instance exists?
95
+ # Or handle carefully within instance methods.
223
96
 
97
+ # --- PowerShell Task Scheduler Helpers --- (These will become methods) ---
224
98
 
225
- def remove_logon_task(username):
226
- """Removes the logon scheduled task for a user."""
227
- logger = _service_instance.log_info if _service_instance else logging.info
228
- task_name = f"{_TASK_NAME_PREFIX}{username}"
229
- safe_task_name = task_name.replace("'", "''")
230
- command = f"Unregister-ScheduledTask -TaskName '{safe_task_name}' -Confirm:$false -ErrorAction SilentlyContinue"
231
- run_powershell_command(command, log_output=False)
232
- logger(f"Attempted removal of scheduled task '{task_name}' for user '{username}'.")
233
- return True
99
+ # _TASK_NAME_PREFIX = "OOTB_UserLogon_" # Move to class
100
+
101
+ # def run_powershell_command(command, log_output=True): ...
102
+ # def create_or_update_logon_task(username, task_command, python_executable): ...
103
+ # def remove_logon_task(username): ...
234
104
 
235
105
  # --- End PowerShell Task Scheduler Helpers ---
236
106
 
@@ -238,6 +108,171 @@ class GuardService(win32serviceutil.ServiceFramework):
238
108
  _svc_name_ = _SERVICE_NAME
239
109
  _svc_display_name_ = _SERVICE_DISPLAY_NAME
240
110
  _svc_description_ = _SERVICE_DESCRIPTION
111
+ _task_name_prefix = "OOTB_UserLogon_" # Class attribute for task prefix
112
+
113
+ # --- Instance Logging Methods ---
114
+ def log_info(self, msg):
115
+ thread_name = threading.current_thread().name
116
+ full_msg = f"[{thread_name}] {msg}"
117
+ logging.info(full_msg)
118
+ try:
119
+ if threading.current_thread().name in ["MainThread", "CommandProcessor"]:
120
+ servicemanager.LogInfoMsg(str(full_msg))
121
+ except Exception as e:
122
+ # Log only to file if event log fails
123
+ logging.warning(f"(Instance) Could not write info to Windows Event Log: {e}")
124
+
125
+ def log_error(self, msg, exc_info=False):
126
+ thread_name = threading.current_thread().name
127
+ full_msg = f"[{thread_name}] {msg}"
128
+ logging.error(full_msg, exc_info=exc_info)
129
+ try:
130
+ if threading.current_thread().name in ["MainThread", "CommandProcessor"]:
131
+ servicemanager.LogErrorMsg(str(full_msg))
132
+ except Exception as e:
133
+ logging.warning(f"(Instance) Could not write error to Windows Event Log: {e}")
134
+ # --- End Instance Logging ---
135
+
136
+ # --- Instance Helper Methods (Moved from module level) ---
137
+ def get_base_python_executable(self):
138
+ """Tries to find python.exe instead of pythonservice.exe if running as service."""
139
+ service_exe = sys.executable
140
+ self.log_info(f"get_base_python_executable: sys.executable is {service_exe}")
141
+ # Normalize path for comparison
142
+ service_exe_lower = service_exe.lower()
143
+
144
+ # Check if running as pythonservice.exe
145
+ if 'pythonservice.exe' in os.path.basename(service_exe_lower):
146
+ # Construct expected python.exe path in the same directory
147
+ dir_name = os.path.dirname(service_exe)
148
+ potential_python_exe = os.path.join(dir_name, 'python.exe')
149
+ self.log_info(f"get_base_python_executable: Checking for python.exe at {potential_python_exe}")
150
+
151
+ if os.path.exists(potential_python_exe):
152
+ self.log_info(f"get_base_python_executable: Found python.exe at {potential_python_exe}")
153
+ # Quote if necessary
154
+ final_exe = potential_python_exe
155
+ if " " in final_exe and not final_exe.startswith('"'):
156
+ return f'"{final_exe}"'
157
+ return final_exe
158
+ else:
159
+ self.log_error(f"get_base_python_executable: Could not find python.exe near pythonservice.exe (checked {potential_python_exe}). Falling back to using {service_exe}.")
160
+ # Fallback to original sys.executable (quoted if needed)
161
+ if " " in service_exe and not service_exe.startswith('"'):
162
+ return f'"{service_exe}"'
163
+ return service_exe
164
+ else: # Not running as pythonservice.exe, assume sys.executable is correct
165
+ self.log_info(f"get_base_python_executable: sys.executable is not pythonservice.exe, using it directly.")
166
+ if " " in service_exe and not service_exe.startswith('"'):
167
+ return f'"{service_exe}"'
168
+ return service_exe
169
+
170
+ def get_python_executable(self):
171
+ # This method is now just an alias for clarity if needed elsewhere,
172
+ # but primary logic uses get_base_python_executable directly.
173
+ # Keep for potential compatibility if anything still calls it.
174
+ return self.get_base_python_executable()
175
+
176
+ def get_pip_executable(self):
177
+ """Tries to locate the pip executable in the same environment."""
178
+ try:
179
+ # Use the potentially corrected python path to find pip's location
180
+ # Note: This assumes pip is relative to python.exe, not pythonservice.exe
181
+ base_python = self.get_base_python_executable()
182
+ python_path = pathlib.Path(base_python.strip('"')) # Use unquoted path
183
+ pip_path = python_path.parent / "Scripts" / "pip.exe"
184
+ self.log_info(f"get_pip_executable: Checking for pip relative to {python_path.parent} at {pip_path}")
185
+
186
+ if pip_path.exists():
187
+ self.log_info(f"get_pip_executable: pip.exe found at {pip_path}")
188
+ pip_exe = str(pip_path)
189
+ if " " in pip_exe and not pip_exe.startswith('"'):
190
+ pip_exe = f'"{pip_exe}"'
191
+ return pip_exe
192
+ else:
193
+ self.log_error(f"get_pip_executable: pip.exe NOT found at {pip_path}. Falling back to '{base_python} -m pip'.")
194
+ pass
195
+ except Exception as e:
196
+ self.log_error(f"get_pip_executable: Error determining pip path: {e}", exc_info=True)
197
+ self.log_error(f"get_pip_executable: Falling back to 'python -m pip' due to error.")
198
+
199
+ # Fallback uses the potentially corrected python path
200
+ return f"{self.get_base_python_executable()} -m pip"
201
+
202
+ # --- PowerShell Methods (Moved from module level) ---
203
+ def run_powershell_command(self, command, log_output=True):
204
+ """Executes a PowerShell command and handles output/errors. Returns True on success."""
205
+ self.log_info(f"Executing PowerShell: {command}")
206
+ try:
207
+ result = subprocess.run(
208
+ ["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", command],
209
+ capture_output=True, text=True, check=True, encoding='utf-8', errors='ignore'
210
+ )
211
+ if log_output and result.stdout:
212
+ self.log_info(f"PowerShell STDOUT:\n{result.stdout.strip()}")
213
+ if log_output and result.stderr:
214
+ self.log_info(f"PowerShell STDERR:\n{result.stderr.strip()}")
215
+ return True
216
+ except FileNotFoundError:
217
+ self.log_error("'powershell.exe' not found. Cannot manage scheduled tasks.")
218
+ return False
219
+ except subprocess.CalledProcessError as e:
220
+ self.log_error(f"PowerShell command failed (Exit Code {e.returncode}):")
221
+ self.log_error(f" Command: {e.cmd}")
222
+ if e.stdout: self.log_error(f" STDOUT: {e.stdout.strip()}")
223
+ if e.stderr: self.log_error(f" STDERR: {e.stderr.strip()}")
224
+ return False
225
+ except Exception as e:
226
+ self.log_error(f"Unexpected error running PowerShell: {e}", exc_info=True)
227
+ return False
228
+
229
+ def create_or_update_logon_task(self, username, task_command, python_executable):
230
+ """Creates or updates a scheduled task to run a command at user logon."""
231
+ task_name = f"{self._task_name_prefix}{username}"
232
+ safe_python_exe = python_executable.replace("'", "''")
233
+ command_parts = task_command.split(' ', 1)
234
+ if len(command_parts) > 1 and command_parts[0] == python_executable:
235
+ safe_task_command_args = command_parts[1].replace("'", "''")
236
+ else:
237
+ safe_task_command_args = task_command.replace(python_executable, "").strip().replace("'", "''")
238
+
239
+ safe_task_name = task_name.replace("'", "''")
240
+ safe_username = username.replace("'", "''")
241
+
242
+ action = f"$Action = New-ScheduledTaskAction -Execute '{safe_python_exe}' -Argument '{safe_task_command_args}'"
243
+ trigger = f"$Trigger = New-ScheduledTaskTrigger -AtLogOn -User '{safe_username}'"
244
+ principal = f"$Principal = New-ScheduledTaskPrincipal -UserId '{safe_username}' -LogonType Interactive"
245
+ settings = "$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -ExecutionTimeLimit ([System.TimeSpan]::Zero) -RunOnlyIfNetworkAvailable:$false"
246
+
247
+ command = f"""
248
+ try {{
249
+ {action}
250
+ {trigger}
251
+ {principal}
252
+ {settings}
253
+ Register-ScheduledTask -TaskName '{safe_task_name}' -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings -Force -ErrorAction Stop
254
+ Write-Host "Scheduled task '{safe_task_name}' registered/updated successfully."
255
+ }} catch {{
256
+ Write-Error "Failed to register/update scheduled task '{safe_task_name}': $_"
257
+ exit 1
258
+ }}
259
+ """
260
+ success = self.run_powershell_command(command)
261
+ if success:
262
+ self.log_info(f"Successfully created/updated scheduled task '{task_name}' for user '{username}'.")
263
+ else:
264
+ self.log_error(f"Failed to create/update scheduled task '{task_name}' for user '{username}'.")
265
+ return success
266
+
267
+ def remove_logon_task(self, username):
268
+ """Removes the logon scheduled task for a user."""
269
+ task_name = f"{self._task_name_prefix}{username}"
270
+ safe_task_name = task_name.replace("'", "''")
271
+ command = f"Unregister-ScheduledTask -TaskName '{safe_task_name}' -Confirm:$false -ErrorAction SilentlyContinue"
272
+ self.run_powershell_command(command, log_output=False)
273
+ self.log_info(f"Attempted removal of scheduled task '{task_name}' for user '{username}'.")
274
+ return True
275
+ # --- End Instance Helper Methods ---
241
276
 
242
277
  def __init__(self, args):
243
278
  global _service_instance
@@ -247,15 +282,18 @@ class GuardService(win32serviceutil.ServiceFramework):
247
282
  self.server_thread = None
248
283
  self.command_queue = queue.Queue()
249
284
  self.command_processor_thread = None
250
- self.session = requests.Session() # For status reporting
285
+ self.session = requests.Session()
251
286
 
252
- self.python_exe = get_python_executable()
253
- self.pip_command_base = get_pip_executable()
287
+ # Initialize paths using instance methods that prefer python.exe
288
+ self.python_exe = self.get_base_python_executable()
289
+ self.pip_command_base = self.get_pip_executable()
290
+ # Construct command using the potentially corrected python.exe path
254
291
  self.ootb_command = f"{self.python_exe} -m {_OOTB_MODULE}"
255
- _service_instance = self # Set global reference
292
+ _service_instance = self
293
+ self.log_info(f"Service initialized. OOTB command set to: {self.ootb_command}")
256
294
 
257
295
  def SvcStop(self):
258
- log_info(f"Service stop requested.")
296
+ self.log_info(f"Service stop requested.")
259
297
  self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
260
298
  self.is_running = False
261
299
  # Signal the command processor thread to stop
@@ -264,7 +302,7 @@ class GuardService(win32serviceutil.ServiceFramework):
264
302
  win32event.SetEvent(self.hWaitStop)
265
303
  # Stopping waitress gracefully from another thread is non-trivial.
266
304
  # We rely on the SCM timeout / process termination for now.
267
- log_info(f"{_SERVICE_NAME} SvcStop: Stop signaled. Server thread will be terminated by SCM.")
305
+ self.log_info(f"{_SERVICE_NAME} SvcStop: Stop signaled. Server thread will be terminated by SCM.")
268
306
 
269
307
 
270
308
  def SvcDoRun(self):
@@ -272,51 +310,51 @@ class GuardService(win32serviceutil.ServiceFramework):
272
310
  servicemanager.PYS_SERVICE_STARTED,
273
311
  (self._svc_name_, ''))
274
312
  try:
275
- log_info(f"{_SERVICE_NAME} starting.")
313
+ self.log_info(f"{_SERVICE_NAME} starting.")
276
314
  # Start the command processor thread
277
315
  self.command_processor_thread = threading.Thread(
278
316
  target=self.process_commands, name="CommandProcessor", daemon=True)
279
317
  self.command_processor_thread.start()
280
- log_info("Command processor thread started.")
318
+ self.log_info("Command processor thread started.")
281
319
 
282
320
  # Start the Flask server (via Waitress) in a separate thread
283
321
  self.server_thread = threading.Thread(
284
322
  target=self.run_server, name="WebServerThread", daemon=True)
285
323
  self.server_thread.start()
286
- log_info(f"Web server thread started, listening on {_LISTEN_HOST}:{_LISTEN_PORT}.")
324
+ self.log_info(f"Web server thread started, listening on {_LISTEN_HOST}:{_LISTEN_PORT}.")
287
325
 
288
- log_info(f"{_SERVICE_NAME} running. Waiting for stop signal.")
326
+ self.log_info(f"{_SERVICE_NAME} running. Waiting for stop signal.")
289
327
  # Keep the main service thread alive waiting for stop signal
290
328
  win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
291
- log_info(f"{_SERVICE_NAME} received stop signal in main thread.")
329
+ self.log_info(f"{_SERVICE_NAME} received stop signal in main thread.")
292
330
 
293
331
  except Exception as e:
294
- log_error(f"Fatal error in SvcDoRun: {e}", exc_info=True)
332
+ self.log_error(f"Fatal error in SvcDoRun: {e}", exc_info=True)
295
333
  self.SvcStop() # Signal stop if possible
296
334
  finally:
297
- log_info(f"{_SERVICE_NAME} SvcDoRun finished.")
335
+ self.log_info(f"{_SERVICE_NAME} SvcDoRun finished.")
298
336
 
299
337
 
300
338
  def run_server(self):
301
339
  """Runs the Flask app using Waitress."""
302
- log_info(f"Waitress server starting on {_LISTEN_HOST}:{_LISTEN_PORT}")
340
+ self.log_info(f"Waitress server starting on {_LISTEN_HOST}:{_LISTEN_PORT}")
303
341
  try:
304
342
  serve(flask_app, host=_LISTEN_HOST, port=_LISTEN_PORT, threads=4)
305
- log_info("Waitress server has stopped.") # Should only happen on shutdown
343
+ self.log_info("Waitress server has stopped.") # Should only happen on shutdown
306
344
  except Exception as e:
307
- log_error(f"Web server thread encountered an error: {e}", exc_info=True)
345
+ self.log_error(f"Web server thread encountered an error: {e}", exc_info=True)
308
346
  # Consider signaling the main thread to stop if the web server fails critically
309
347
  # For now, just log the error.
310
348
 
311
349
 
312
350
  def process_commands(self):
313
351
  """Worker thread to process commands from the queue."""
314
- log_info("Command processor thread starting.")
352
+ self.log_info("Command processor thread starting.")
315
353
  while self.is_running:
316
354
  try:
317
355
  item = self.command_queue.get(block=True, timeout=1) # Add timeout to check is_running periodically
318
356
  if item is None:
319
- log_info("Command processor received stop signal.")
357
+ self.log_info("Command processor received stop signal.")
320
358
  break # Exit loop
321
359
 
322
360
  command_id, command = item
@@ -324,7 +362,7 @@ class GuardService(win32serviceutil.ServiceFramework):
324
362
  target = command.get("target_user", "all_active")
325
363
  status = "failed_unknown" # Default
326
364
 
327
- log_info(f"Dequeued Command ID {command_id}: action='{action}', target='{target}'")
365
+ self.log_info(f"Dequeued Command ID {command_id}: action='{action}', target='{target}'")
328
366
 
329
367
  try:
330
368
  if action == "update":
@@ -334,10 +372,10 @@ class GuardService(win32serviceutil.ServiceFramework):
334
372
  elif action == "start_ootb":
335
373
  status = self.handle_start(target)
336
374
  else:
337
- log_error(f"Unknown action in queue: {action}")
375
+ self.log_error(f"Unknown action in queue: {action}")
338
376
  status = "failed_unknown_action"
339
377
  except Exception as handler_ex:
340
- log_error(f"Exception processing Command ID {command_id} ({action}): {handler_ex}", exc_info=True)
378
+ self.log_error(f"Exception processing Command ID {command_id} ({action}): {handler_ex}", exc_info=True)
341
379
  status = "failed_exception"
342
380
  finally:
343
381
  self.report_command_status(command_id, status)
@@ -347,17 +385,17 @@ class GuardService(win32serviceutil.ServiceFramework):
347
385
  # Timeout occurred, just loop again and check self.is_running
348
386
  continue
349
387
  except Exception as e:
350
- log_error(f"Error in command processing loop: {e}", exc_info=True)
388
+ self.log_error(f"Error in command processing loop: {e}", exc_info=True)
351
389
  if self.is_running:
352
390
  time.sleep(5)
353
391
 
354
- log_info("Command processor thread finished.")
392
+ self.log_info("Command processor thread finished.")
355
393
 
356
394
 
357
395
  def report_command_status(self, command_id, status, details=""):
358
396
  """Sends command status back to the server."""
359
397
  if not _SERVER_STATUS_REPORT_URL:
360
- log_error("No server status report URL configured. Skipping report.")
398
+ self.log_error("No server status report URL configured. Skipping report.")
361
399
  return
362
400
 
363
401
  payload = {
@@ -366,41 +404,40 @@ class GuardService(win32serviceutil.ServiceFramework):
366
404
  "details": details,
367
405
  "machine_id": os.getenv('COMPUTERNAME', 'unknown_guard')
368
406
  }
369
- log_info(f"Reporting status for command {command_id}: {status}")
407
+ self.log_info(f"Reporting status for command {command_id}: {status}")
370
408
  try:
371
409
  response = self.session.post(_SERVER_STATUS_REPORT_URL, json=payload, timeout=15)
372
410
  response.raise_for_status()
373
- log_info(f"Status report for command {command_id} accepted by server.")
411
+ self.log_info(f"Status report for command {command_id} accepted by server.")
374
412
  except requests.exceptions.RequestException as e:
375
- log_error(f"Failed to report status for command {command_id}: {e}")
413
+ self.log_error(f"Failed to report status for command {command_id}: {e}")
376
414
  except Exception as e:
377
- log_error(f"Unexpected error reporting status for command {command_id}: {e}", exc_info=True)
415
+ self.log_error(f"Unexpected error reporting status for command {command_id}: {e}", exc_info=True)
378
416
 
379
- # --- Command Handlers --- Copying full implementation from previous version
417
+ # --- Command Handlers --- Now call self. for helpers
380
418
 
381
419
  def handle_update(self):
382
- log_info("Executing OOTB update...")
420
+ self.log_info("Executing OOTB update...")
383
421
  if not self.pip_command_base:
384
- log_error("Cannot update: pip command not found.")
422
+ self.log_error("Cannot update: pip command not found.")
385
423
  return "failed_pip_not_found"
386
424
 
387
425
  update_command = f"{self.pip_command_base} install --upgrade --no-cache-dir {_PACKAGE_NAME}"
388
- log_info(f"Running update command: {update_command}")
426
+ self.log_info(f"Running update command: {update_command}")
389
427
  try:
390
428
  result = subprocess.run(update_command, shell=True, capture_output=True, text=True, check=True, timeout=300, encoding='utf-8')
391
- log_info(f"Update successful: \nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
429
+ self.log_info(f"Update successful: \nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
392
430
  return "success"
393
431
  except subprocess.CalledProcessError as e:
394
- log_error(f"Update failed (Exit Code {e.returncode}):\nSTDOUT:\n{e.stdout}\nSTDERR:\n{e.stderr}")
432
+ self.log_error(f"Update failed (Exit Code {e.returncode}):\nSTDOUT:\n{e.stdout}\nSTDERR:\n{e.stderr}")
395
433
  return f"failed_exit_{e.returncode}"
396
434
  except subprocess.TimeoutExpired:
397
- log_error(f"Update command timed out.")
435
+ self.log_error(f"Update command timed out.")
398
436
  return "failed_timeout"
399
437
  except Exception as e:
400
- log_error(f"Unexpected error during update: {e}", exc_info=True)
438
+ self.log_error(f"Unexpected error during update: {e}", exc_info=True)
401
439
  return "failed_exception"
402
440
 
403
-
404
441
  def _get_ootb_processes(self, target_user="all_active"):
405
442
  ootb_procs = []
406
443
  target_pid_list = []
@@ -412,10 +449,11 @@ class GuardService(win32serviceutil.ServiceFramework):
412
449
  target_users.add(username.lower())
413
450
  else:
414
451
  target_users.add(target_user.lower())
415
-
416
- log_info(f"Searching for OOTB processes for users: {target_users}")
417
-
418
- python_exe_path = self.python_exe.strip('"') # Get unquoted path for comparison
452
+ self.log_info(f"Searching for OOTB processes for users: {target_users}")
453
+
454
+ # Use the potentially corrected python.exe path for matching
455
+ python_exe_path_for_check = self.python_exe.strip('"')
456
+ self.log_info(f"_get_ootb_processes: Checking against python path: {python_exe_path_for_check}")
419
457
 
420
458
  for proc in psutil.process_iter(['pid', 'name', 'username', 'cmdline', 'exe']):
421
459
  try:
@@ -426,22 +464,20 @@ class GuardService(win32serviceutil.ServiceFramework):
426
464
 
427
465
  if proc_username in target_users:
428
466
  cmdline = ' '.join(pinfo['cmdline']) if pinfo['cmdline'] else ''
429
- # Check if the process executable matches our python path AND module is in cmdline
430
- if pinfo['exe'] and pinfo['exe'] == python_exe_path and _OOTB_MODULE in cmdline:
431
- log_info(f"Found matching OOTB process: PID={pinfo['pid']}, User={pinfo['username']}, Cmd={cmdline}")
467
+ # Check if the process executable matches our corrected python path AND module is in cmdline
468
+ if pinfo['exe'] and pinfo['exe'] == python_exe_path_for_check and _OOTB_MODULE in cmdline:
469
+ self.log_info(f"Found matching OOTB process: PID={pinfo['pid']}, User={pinfo['username']}, Cmd={cmdline}")
432
470
  ootb_procs.append(proc)
433
471
  target_pid_list.append(pinfo['pid'])
434
-
435
472
  except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
436
473
  continue
437
- log_info(f"Found {len(ootb_procs)} OOTB process(es) matching criteria: {target_pid_list}")
474
+ self.log_info(f"Found {len(ootb_procs)} OOTB process(es) matching criteria: {target_pid_list}")
438
475
  except Exception as e:
439
- log_error(f"Error enumerating processes: {e}", exc_info=True)
476
+ self.log_error(f"Error enumerating processes: {e}", exc_info=True)
440
477
  return ootb_procs
441
478
 
442
-
443
479
  def handle_stop(self, target_user="all_active"):
444
- log_info(f"Executing stop OOTB for target '{target_user}'...")
480
+ self.log_info(f"Executing stop OOTB for target '{target_user}'...")
445
481
  stop_results = {} # Track results per user {username: (task_status, immediate_status)}
446
482
  failed_users = set()
447
483
 
@@ -458,26 +494,26 @@ class GuardService(win32serviceutil.ServiceFramework):
458
494
  if user:
459
495
  active_sessions[user.lower()] = session['SessionId']
460
496
  except Exception as query_err:
461
- log_error(f"Could not query session {session['SessionId']} during stop: {query_err}")
497
+ self.log_error(f"Could not query session {session['SessionId']} during stop: {query_err}")
462
498
  except Exception as user_enum_err:
463
- log_error(f"Error enumerating users/sessions during stop: {user_enum_err}", exc_info=True)
499
+ self.log_error(f"Error enumerating users/sessions during stop: {user_enum_err}", exc_info=True)
464
500
  return "failed_user_enumeration"
465
501
 
466
- log_info(f"Stop target: '{target_user}'. Active sessions: {active_sessions}")
502
+ self.log_info(f"Stop target: '{target_user}'. Active sessions: {active_sessions}")
467
503
 
468
504
  target_users_normalized = set()
469
505
  if target_user == "all_active":
470
506
  # Target only currently active users for stop all
471
507
  target_users_normalized = set(active_sessions.keys())
472
- log_info(f"Stop targeting all active users: {target_users_normalized}")
508
+ self.log_info(f"Stop targeting all active users: {target_users_normalized}")
473
509
  else:
474
510
  # Target the specific user, regardless of active status (for task removal)
475
511
  normalized_target = target_user.lower()
476
512
  target_users_normalized.add(normalized_target)
477
- log_info(f"Stop targeting specific user: {normalized_target}")
513
+ self.log_info(f"Stop targeting specific user: {normalized_target}")
478
514
 
479
515
  if not target_users_normalized:
480
- log_info("No target users identified for stop.")
516
+ self.log_info("No target users identified for stop.")
481
517
  return "failed_no_target_users" # Or success if none were targeted?
482
518
 
483
519
  # --- Process each target user ---
@@ -486,15 +522,15 @@ class GuardService(win32serviceutil.ServiceFramework):
486
522
  immediate_stop_status = "stop_not_attempted"
487
523
  stopped_count = 0
488
524
 
489
- log_info(f"Processing stop for user '{user}'...")
525
+ self.log_info(f"Processing stop for user '{user}'...")
490
526
 
491
527
  # 1. Always try to remove the scheduled task
492
528
  try:
493
529
  # remove_logon_task always returns True for now, just logs attempt
494
- remove_logon_task(user)
530
+ self.remove_logon_task(user)
495
531
  task_removed_status = "task_removed_attempted"
496
532
  except Exception as task_err:
497
- log_error(f"Exception removing scheduled task for {user}: {task_err}", exc_info=True)
533
+ self.log_error(f"Exception removing scheduled task for {user}: {task_err}", exc_info=True)
498
534
  task_removed_status = "task_exception"
499
535
  failed_users.add(user)
500
536
  # Continue to try and stop process if active
@@ -504,37 +540,37 @@ class GuardService(win32serviceutil.ServiceFramework):
504
540
 
505
541
  if is_active:
506
542
  immediate_stop_status = "stop_attempted"
507
- log_info(f"User '{user}' is active. Attempting to terminate OOTB process(es)...")
543
+ self.log_info(f"User '{user}' is active. Attempting to terminate OOTB process(es)...")
508
544
  # Pass the specific username to _get_ootb_processes
509
545
  procs_to_stop = self._get_ootb_processes(user)
510
546
 
511
547
  if not procs_to_stop:
512
- log_info(f"No running OOTB processes found for active user '{user}'.")
548
+ self.log_info(f"No running OOTB processes found for active user '{user}'.")
513
549
  immediate_stop_status = "stop_skipped_not_running"
514
550
  else:
515
- log_info(f"Found {len(procs_to_stop)} process(es) for user '{user}' to stop.")
551
+ self.log_info(f"Found {len(procs_to_stop)} process(es) for user '{user}' to stop.")
516
552
  for proc in procs_to_stop:
517
553
  try:
518
554
  pid = proc.pid # Get pid before potential termination
519
555
  username = proc.info.get('username', 'unknown_user')
520
- log_info(f"Terminating process PID={pid}, User={username}")
556
+ self.log_info(f"Terminating process PID={pid}, User={username}")
521
557
  proc.terminate()
522
558
  try:
523
559
  proc.wait(timeout=3)
524
- log_info(f"Process PID={pid} terminated successfully.")
560
+ self.log_info(f"Process PID={pid} terminated successfully.")
525
561
  stopped_count += 1
526
562
  except psutil.TimeoutExpired:
527
- log_error(f"Process PID={pid} did not terminate gracefully, killing.")
563
+ self.log_error(f"Process PID={pid} did not terminate gracefully, killing.")
528
564
  proc.kill()
529
565
  stopped_count += 1
530
566
  except psutil.NoSuchProcess:
531
- log_info(f"Process PID={pid} already terminated.")
567
+ self.log_info(f"Process PID={pid} already terminated.")
532
568
  # Don't increment stopped_count here as we didn't stop it now
533
569
  except psutil.AccessDenied:
534
- log_error(f"Access denied trying to terminate process PID={pid}.")
570
+ self.log_error(f"Access denied trying to terminate process PID={pid}.")
535
571
  failed_users.add(user) # Mark user as failed if stop fails
536
572
  except Exception as e:
537
- log_error(f"Error stopping process PID={pid}: {e}", exc_info=True)
573
+ self.log_error(f"Error stopping process PID={pid}: {e}", exc_info=True)
538
574
  failed_users.add(user) # Mark user as failed
539
575
 
540
576
  # Determine status based on how many were found vs stopped
@@ -546,7 +582,7 @@ class GuardService(win32serviceutil.ServiceFramework):
546
582
  immediate_stop_status = f"stop_partial_terminated_{stopped_count}_of_{len(procs_to_stop)}"
547
583
 
548
584
  else: # User not active
549
- log_info(f"User '{user}' is not active. Skipping immediate process stop (task removal attempted).")
585
+ self.log_info(f"User '{user}' is not active. Skipping immediate process stop (task removal attempted).")
550
586
  immediate_stop_status = "stop_skipped_inactive"
551
587
 
552
588
  # Record final results for this user
@@ -559,7 +595,7 @@ class GuardService(win32serviceutil.ServiceFramework):
559
595
  if not stop_results: final_status = "no_targets_processed"
560
596
  if len(failed_users) == total_processed and total_processed > 0 : final_status = "failed"
561
597
 
562
- log_info(f"Finished stopping OOTB. Overall Status: {final_status}. Results: {stop_results}")
598
+ self.log_info(f"Finished stopping OOTB. Overall Status: {final_status}. Results: {stop_results}")
563
599
  try:
564
600
  details = json.dumps(stop_results)
565
601
  except Exception:
@@ -567,12 +603,12 @@ class GuardService(win32serviceutil.ServiceFramework):
567
603
  return f"{final_status}::{details}" # Use :: as separator
568
604
 
569
605
  except Exception as e:
570
- log_error(f"Error during combined stop OOTB process: {e}", exc_info=True)
606
+ self.log_error(f"Error during combined stop OOTB process: {e}", exc_info=True)
571
607
  return "failed_exception"
572
608
 
573
609
 
574
610
  def handle_start(self, target_user="all_active"):
575
- log_info(f"Executing start OOTB for target '{target_user}'...")
611
+ self.log_info(f"Executing start OOTB for target '{target_user}'...")
576
612
  start_results = {} # Track results per user {username: (task_status, immediate_status)}
577
613
  failed_users = set()
578
614
 
@@ -594,18 +630,18 @@ class GuardService(win32serviceutil.ServiceFramework):
594
630
  if user:
595
631
  active_sessions[user.lower()] = session['SessionId']
596
632
  except Exception as query_err:
597
- log_error(f"Could not query session {session['SessionId']}: {query_err}")
633
+ self.log_error(f"Could not query session {session['SessionId']}: {query_err}")
598
634
  except Exception as user_enum_err:
599
- log_error(f"Error enumerating users/sessions: {user_enum_err}", exc_info=True)
635
+ self.log_error(f"Error enumerating users/sessions: {user_enum_err}", exc_info=True)
600
636
  return "failed_user_enumeration"
601
637
 
602
- log_info(f"Found active user sessions: {active_sessions}")
638
+ self.log_info(f"Found active user sessions: {active_sessions}")
603
639
 
604
640
  target_users_normalized = set()
605
641
  if target_user == "all_active":
606
642
  # If targeting all_active, only target those CURRENTLY active
607
643
  target_users_normalized = set(active_sessions.keys())
608
- log_info(f"Targeting all active users: {target_users_normalized}")
644
+ self.log_info(f"Targeting all active users: {target_users_normalized}")
609
645
  else:
610
646
  normalized_target = target_user.lower()
611
647
  # Check if the target user actually exists on the system, even if inactive
@@ -613,13 +649,13 @@ class GuardService(win32serviceutil.ServiceFramework):
613
649
  # Let's assume admin provides a valid username for specific targeting.
614
650
  # if normalized_target in all_system_users: # Removing this check, assume valid user input
615
651
  target_users_normalized.add(normalized_target)
616
- log_info(f"Targeting specific user: {normalized_target}")
652
+ self.log_info(f"Targeting specific user: {normalized_target}")
617
653
  # else:
618
654
  # log_error(f"Target user '{target_user}' does not appear to exist on this system based on psutil.")
619
655
  # return "failed_user_does_not_exist"
620
656
 
621
657
  if not target_users_normalized:
622
- log_info("No target users identified (or none active for 'all_active').")
658
+ self.log_info("No target users identified (or none active for 'all_active').")
623
659
  # If target was specific user but they weren't found active, still try task?
624
660
  # Let's proceed to task creation anyway for specific user case.
625
661
  if target_user != "all_active": target_users_normalized.add(target_user.lower())
@@ -639,8 +675,8 @@ class GuardService(win32serviceutil.ServiceFramework):
639
675
  running_procs_by_user[user_lower] = running_procs_by_user.get(user_lower, 0) + 1
640
676
  except Exception: pass
641
677
  except Exception as e:
642
- log_error(f"Error checking existing processes: {e}")
643
- log_info(f"Users currently running OOTB: {running_procs_by_user}")
678
+ self.log_error(f"Error checking existing processes: {e}")
679
+ self.log_info(f"Users currently running OOTB: {running_procs_by_user}")
644
680
 
645
681
  # --- Process each target user ---
646
682
  for user in target_users_normalized:
@@ -648,14 +684,14 @@ class GuardService(win32serviceutil.ServiceFramework):
648
684
  immediate_start_status = "start_not_attempted"
649
685
  token = None # Ensure token is reset/defined
650
686
 
651
- log_info(f"Processing start for user '{user}'...")
687
+ self.log_info(f"Processing start for user '{user}'...")
652
688
 
653
689
  # 1. Always try to create/update the scheduled task
654
690
  try:
655
- task_created = create_or_update_logon_task(user, self.ootb_command, self.python_exe)
691
+ task_created = self.create_or_update_logon_task(user, self.ootb_command, self.python_exe)
656
692
  task_created_status = "task_success" if task_created else "task_failed"
657
693
  except Exception as task_err:
658
- log_error(f"Exception creating/updating scheduled task for {user}: {task_err}", exc_info=True)
694
+ self.log_error(f"Exception creating/updating scheduled task for {user}: {task_err}", exc_info=True)
659
695
  task_created_status = "task_exception"
660
696
  failed_users.add(user)
661
697
  # Continue to potentially try immediate start IF user is active?
@@ -671,7 +707,7 @@ class GuardService(win32serviceutil.ServiceFramework):
671
707
  if is_active:
672
708
  if not is_running:
673
709
  immediate_start_status = "start_attempted"
674
- log_info(f"User '{user}' is active and not running OOTB. Attempting immediate start...")
710
+ self.log_info(f"User '{user}' is active and not running OOTB. Attempting immediate start...")
675
711
  try:
676
712
  session_id = active_sessions[user]
677
713
  token = win32ts.WTSQueryUserToken(session_id)
@@ -686,21 +722,21 @@ class GuardService(win32serviceutil.ServiceFramework):
686
722
  token, self.python_exe, self.ootb_command,
687
723
  None, None, False, creation_flags, env, user_profile_dir, startup
688
724
  )
689
- log_info(f"CreateProcessAsUser call succeeded for user '{user}' (PID: {dwPid}). Checking existence...")
725
+ self.log_info(f"CreateProcessAsUser call succeeded for user '{user}' (PID: {dwPid}). Checking existence...")
690
726
  win32api.CloseHandle(hProcess)
691
727
  win32api.CloseHandle(hThread)
692
728
 
693
729
  time.sleep(1)
694
730
  if psutil.pid_exists(dwPid):
695
- log_info(f"Immediate start succeeded for user '{user}' (PID {dwPid}).")
731
+ self.log_info(f"Immediate start succeeded for user '{user}' (PID {dwPid}).")
696
732
  immediate_start_status = "start_success"
697
733
  else:
698
- log_error(f"Immediate start failed for user '{user}': Process {dwPid} exited immediately.")
734
+ self.log_error(f"Immediate start failed for user '{user}': Process {dwPid} exited immediately.")
699
735
  immediate_start_status = "start_failed_exited"
700
736
  failed_users.add(user)
701
737
 
702
738
  except Exception as proc_err:
703
- log_error(f"Exception during immediate start for user '{user}': {proc_err}", exc_info=True)
739
+ self.log_error(f"Exception during immediate start for user '{user}': {proc_err}", exc_info=True)
704
740
  immediate_start_status = "start_failed_exception"
705
741
  failed_users.add(user)
706
742
  finally:
@@ -708,10 +744,10 @@ class GuardService(win32serviceutil.ServiceFramework):
708
744
  try: win32api.CloseHandle(token)
709
745
  except: pass
710
746
  else: # User is active but already running
711
- log_info(f"User '{user}' is active but OOTB is already running. Skipping immediate start.")
747
+ self.log_info(f"User '{user}' is active but OOTB is already running. Skipping immediate start.")
712
748
  immediate_start_status = "start_skipped_already_running"
713
749
  else: # User is not active
714
- log_info(f"User '{user}' is not active. Skipping immediate start (task created/updated).")
750
+ self.log_info(f"User '{user}' is not active. Skipping immediate start (task created/updated).")
715
751
  immediate_start_status = "start_skipped_inactive"
716
752
 
717
753
  # Record final results for this user
@@ -732,7 +768,7 @@ class GuardService(win32serviceutil.ServiceFramework):
732
768
  else:
733
769
  final_status = "failed_task_user_inactive"
734
770
 
735
- log_info(f"Finished starting OOTB. Overall Status: {final_status}. Results: {start_results}")
771
+ self.log_info(f"Finished starting OOTB. Overall Status: {final_status}. Results: {start_results}")
736
772
  # Return detailed results as a JSON string for easier parsing/logging server-side
737
773
  try:
738
774
  details = json.dumps(start_results)
@@ -741,13 +777,13 @@ class GuardService(win32serviceutil.ServiceFramework):
741
777
  return f"{final_status}::{details}"
742
778
 
743
779
  except Exception as e:
744
- log_error(f"Error during combined start OOTB process: {e}", exc_info=True)
780
+ self.log_error(f"Error during combined start OOTB process: {e}", exc_info=True)
745
781
  return "failed_exception"
746
782
 
747
783
  # --- Main Execution Block ---
748
784
  if __name__ == '__main__':
749
785
  if len(sys.argv) > 1 and sys.argv[1] == 'debug':
750
- log_info("Starting service in debug mode...")
786
+ self.log_info("Starting service in debug mode...")
751
787
  print(f"Running Flask server via Waitress on {_LISTEN_HOST}:{_LISTEN_PORT} for debugging...")
752
788
  print("Service logic (command processing) will NOT run in this mode.")
753
789
  print("Use this primarily to test the '/command' endpoint receiving POSTs.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: computer-use-ootb-internal
3
- Version: 0.0.120
3
+ Version: 0.0.122
4
4
  Summary: Computer Use OOTB
5
5
  Author-email: Siyuan Hu <siyuan.hu.sg@gmail.com>
6
6
  Requires-Python: >=3.11
@@ -3,7 +3,7 @@ computer_use_ootb_internal/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4Tv
3
3
  computer_use_ootb_internal/app_teachmode.py,sha256=PClMS7X6zRGzY7YPOV6Zxkfv5BajLVqmBiv-mOBVxkw,22164
4
4
  computer_use_ootb_internal/app_teachmode_gradio.py,sha256=cmFpBrkdlZxOQADWveVdIaaNqaBD8IVs-xNLJogU7F8,7909
5
5
  computer_use_ootb_internal/dependency_check.py,sha256=y8RMEP6RXQzTgU1MS_1piBLtz4J-Hfn9RjUZg59dyvo,1333
6
- computer_use_ootb_internal/guard_service.py,sha256=1CiueJvILdqPB1Ncxq9lUIkhUk7yZ-AgVBEwnbmkCNU,39691
6
+ computer_use_ootb_internal/guard_service.py,sha256=rxTdjzhcZf-Osohnf9xnKFTiKUr-DJAgFHPEGfmFDVI,42789
7
7
  computer_use_ootb_internal/requirements-lite.txt,sha256=5DAHomz4A_P2BmTIXNkNqkHbnIF0AyZ4_1XAlb1LaYs,290
8
8
  computer_use_ootb_internal/run_teachmode_ootb_args.py,sha256=7Dj0iY4GG7P03tRKYJ2x9Yvt-PE-b7uyjCAed3SaF3Y,7086
9
9
  computer_use_ootb_internal/service_manager.py,sha256=SD8jzfn0VVXBOr_nP6zmBWSC2TzrU_sp2e5JJkSlQFU,9734
@@ -34,7 +34,7 @@ computer_use_ootb_internal/computer_use_demo/tools/run.py,sha256=xhXdnBK1di9muaO
34
34
  computer_use_ootb_internal/computer_use_demo/tools/screen_capture.py,sha256=L8qfvtUkPPQGt92N-2Zfw5ZTDBzLsDps39uMnX3_uSA,6857
35
35
  computer_use_ootb_internal/preparation/__init__.py,sha256=AgtGHcBpiTkxJjF0xwcs3yyQ6SyUvhL3G0vD2XO-zJw,63
36
36
  computer_use_ootb_internal/preparation/star_rail_prepare.py,sha256=s1VWszcTnJAKxqCHFlaOEwPkqVSrkiFx_yKpWSnSbHs,2649
37
- computer_use_ootb_internal-0.0.120.dist-info/METADATA,sha256=51EX1JLqwmtsEHPfkcQ4Ie2exLtQdAMh6YTigZuhxhU,1048
38
- computer_use_ootb_internal-0.0.120.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
39
- computer_use_ootb_internal-0.0.120.dist-info/entry_points.txt,sha256=bXfyAU_qq-G1EiEgAQEioXvgEdRCFxaTooqdDD9Y4OA,258
40
- computer_use_ootb_internal-0.0.120.dist-info/RECORD,,
37
+ computer_use_ootb_internal-0.0.122.dist-info/METADATA,sha256=l4cD__IPEj-hiKqiaFO49w016NHjVZ1y7cza5WHKDYc,1048
38
+ computer_use_ootb_internal-0.0.122.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
39
+ computer_use_ootb_internal-0.0.122.dist-info/entry_points.txt,sha256=bXfyAU_qq-G1EiEgAQEioXvgEdRCFxaTooqdDD9Y4OA,258
40
+ computer_use_ootb_internal-0.0.122.dist-info/RECORD,,