computer-use-ootb-internal 0.0.147__tar.gz → 0.0.148__tar.gz

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 (39) hide show
  1. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/PKG-INFO +1 -1
  2. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/pyproject.toml +1 -1
  3. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/app_teachmode.py +23 -36
  4. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/guard_service.py +120 -97
  5. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/.gitignore +0 -0
  6. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/README.md +0 -0
  7. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/__init__.py +0 -0
  8. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/animation/click_animation.py +0 -0
  9. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/animation/icons8-select-cursor-transparent-96.gif +0 -0
  10. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/animation/test_animation.py +0 -0
  11. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/executor/teachmode_executor.py +0 -0
  12. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/__init__.py +0 -0
  13. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/gui_capture.py +0 -0
  14. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/utils.py +0 -0
  15. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/__init__.py +0 -0
  16. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/screenshot_cli.py +0 -0
  17. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/screenshot_service.py +0 -0
  18. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/llm_utils.py +0 -0
  19. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/oai.py +0 -0
  20. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/run_litellm.py +0 -0
  21. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/run_llm.py +0 -0
  22. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/__init__.py +0 -0
  23. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/aws_request.py +0 -0
  24. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/base.py +0 -0
  25. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/bash.py +0 -0
  26. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/collection.py +0 -0
  27. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/colorful_text.py +0 -0
  28. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/computer.py +0 -0
  29. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/computer_marbot.py +0 -0
  30. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/edit.py +0 -0
  31. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/run.py +0 -0
  32. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/computer_use_demo/tools/screen_capture.py +0 -0
  33. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/dependency_check.py +0 -0
  34. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/preparation/__init__.py +0 -0
  35. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/preparation/star_rail_prepare.py +0 -0
  36. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/requirements-lite.txt +0 -0
  37. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/run_teachmode_ootb_args.py +0 -0
  38. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/service_manager.py +0 -0
  39. {computer_use_ootb_internal-0.0.147 → computer_use_ootb_internal-0.0.148}/src/computer_use_ootb_internal/signal_connection.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: computer-use-ootb-internal
3
- Version: 0.0.147
3
+ Version: 0.0.148
4
4
  Summary: Computer Use OOTB
5
5
  Author-email: Siyuan Hu <siyuan.hu.sg@gmail.com>
6
6
  Requires-Python: >=3.11
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "computer-use-ootb-internal"
7
- version = "0.0.147"
7
+ version = "0.0.148"
8
8
  description = "Computer Use OOTB"
9
9
  authors = [{ name = "Siyuan Hu", email = "siyuan.hu.sg@gmail.com" }]
10
10
  requires-python = ">=3.11"
@@ -123,7 +123,6 @@ class SharedState:
123
123
  self.trace_id = args.trace_id
124
124
  self.api_keys = args.api_keys
125
125
  self.server_url = args.server_url
126
- self.port = getattr(args, 'port', None)
127
126
  self.message_queue = []
128
127
  self.is_processing = False
129
128
  self.should_stop = False
@@ -207,8 +206,6 @@ async def update_parameters(request: Request):
207
206
  shared_state.trace_id = getattr(shared_state.args, 'trace_id', "build_scroll_combat")
208
207
  shared_state.api_keys = getattr(shared_state.args, 'api_keys', "sk-proj-1234567890")
209
208
  shared_state.server_url = getattr(shared_state.args, 'server_url', "http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com")
210
- # Include the specified port if available
211
- shared_state.port = getattr(shared_state.args, 'port', None)
212
209
 
213
210
  log_ootb_request(shared_state.server_url, "update_params", data)
214
211
 
@@ -496,8 +493,9 @@ def main():
496
493
  parser.add_argument("--trace_id", type=str, default="build_scroll_combat", help="Trace ID for the session")
497
494
  parser.add_argument("--api_keys", type=str, default="sk-proj-1234567890", help="API keys")
498
495
  parser.add_argument("--server_url", type=str, default="http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com", help="Server URL for the session")
499
- # Add argument to directly specify the port
500
- parser.add_argument("-p", "--port", type=int, default=None, help="Specify the exact port to run the server on, overriding username-based calculation.")
496
+ # Add arguments for port override and target user
497
+ parser.add_argument("-p", "--port", type=int, default=None, help="Specify the port to run the server on, overriding user-based calculation.")
498
+ parser.add_argument("--target_user", type=str, default=None, help="Specify the target username for port calculation if --port is not given.")
501
499
 
502
500
  args = parser.parse_args()
503
501
 
@@ -509,49 +507,38 @@ def main():
509
507
  if not hasattr(args, 'trace_id'): args.trace_id = "unknown_trace"
510
508
  if not hasattr(args, 'api_keys'): args.api_keys = "none"
511
509
  if not hasattr(args, 'server_url'): args.server_url = "none"
512
- # Ensure the port arg exists
513
- if not hasattr(args, 'port'): args.port = None
514
510
 
515
511
  shared_state = SharedState(args)
516
512
  rate_limiter = RateLimiter(interval_seconds=2) # Re-initialize rate limiter
517
513
  logging.info(f"Shared state initialized for user: {args.user_id}")
518
514
 
519
- # --- Port Determination ---
520
- port = args.port # Get port from argument first
515
+ # --- Port Calculation Logic ---
516
+ port = 7888 # Default port
521
517
  host = "0.0.0.0" # Listen on all interfaces
522
-
523
- if port is not None:
518
+
519
+ if args.port is not None:
520
+ port = args.port
524
521
  logging.info(f"Using specified port from --port argument: {port}")
525
- elif platform.system() == "Windows":
526
- # Fallback to username calculation ONLY if --port was not provided
527
- port = 7888 # Default port for fallback calculation
522
+ elif platform.system() == "Windows" and args.target_user is not None:
528
523
  try:
529
- username_to_check = os.environ.get("USERNAME")
530
- if username_to_check:
531
- username = username_to_check.lower()
532
- logging.info(f"Determining port based on current user environment: {username}")
533
- if username == "altair":
534
- port = 14000
535
- elif username.startswith("guest") and username[5:].isdigit():
536
- num = int(username[5:])
537
- if 1 <= num <= 10:
538
- port = 14000 + num
539
- else:
540
- logging.warning(f"Guest user number {num} out of range (1-10), using default port {port}.")
541
- elif username == "administrator":
542
- logging.info(f"Running as Administrator, using default port {port}.")
524
+ # Use the --target_user argument for calculation
525
+ username = args.target_user.lower()
526
+ logging.info(f"Determining port based on Windows username from --target_user: {username}")
527
+ if username == "altair":
528
+ port = 14000
529
+ elif username.startswith("guest") and username[5:].isdigit():
530
+ num = int(username[5:])
531
+ if 1 <= num <= 10: # Assuming max 10 guests for this range
532
+ port = 14000 + num
543
533
  else:
544
- logging.info(f"Username '{username}' doesn't match specific rules, using default port {port}.")
534
+ logging.warning(f"Guest user number {num} out of range (1-10), using default port {port}.")
545
535
  else:
546
- logging.warning(f"USERNAME environment variable not found. Using default port {port}.")
536
+ logging.info(f"Username '{username}' doesn't match specific rules, using default port {port}.")
547
537
  except Exception as e:
548
- logging.error(f"Error calculating port from username: {e}. Using default port {port}.", exc_info=True)
538
+ logging.error(f"Error determining port from --target_user '{args.target_user}': {e}. Using default port {port}.", exc_info=True)
549
539
  else:
550
- # Non-windows, or Windows with no username found, and no --port arg
551
- port = 7888 # Default port
552
- logging.info(f"Not on Windows or username not found, using default port {port}.")
553
-
554
- # --- End Port Determination ---
540
+ logging.info(f"--port not specified, and not on Windows or --target_user not specified. Using default port {port}.")
541
+ # --- End Port Calculation ---
555
542
 
556
543
  logging.info(f"Final Host={host}, Port={port}")
557
544
 
@@ -52,9 +52,6 @@ _service_instance = None
52
52
  # --- Flask App Definition ---
53
53
  flask_app = Flask(__name__)
54
54
 
55
- # Default port constant
56
- _DEFAULT_OOTB_PORT = 7888
57
-
58
55
  @flask_app.route('/command', methods=['POST'])
59
56
  def receive_command():
60
57
  global _service_instance
@@ -666,7 +663,9 @@ class GuardService(win32serviceutil.ServiceFramework):
666
663
  trigger_results = {}
667
664
  for user in target_users_normalized:
668
665
  self.log_info(f"Calling internal start trigger for user: {user}")
669
- # Queue the start action using the command queue
666
+ # Call the core logic directly (this is now synchronous within the handler)
667
+ # Or queue it? Queuing might be better to avoid blocking the handler if many users.
668
+ # Let's stick to the queue approach from the internal endpoint:
670
669
  internal_command = {
671
670
  "action": "_internal_start_ootb",
672
671
  "target_user": user
@@ -683,15 +682,45 @@ class GuardService(win32serviceutil.ServiceFramework):
683
682
 
684
683
 
685
684
  def _trigger_start_for_user(self, username):
686
- """Core logic to start OOTB for a single user using elevated scheduled task."""
685
+ """Core logic to start OOTB for a single user. Called internally."""
687
686
  user = username.lower() # Ensure lowercase
688
687
  self.log_info(f"Internal trigger: Starting OOTB check for user '{user}'...")
689
- task_name = f"OOTB_UserConnect_{user}" # Task name used for start
688
+ task_created_status = "task_unknown"
689
+ immediate_start_status = "start_not_attempted"
690
690
  final_status = "failed_unknown"
691
+ calculated_port = 7888 # Default port
692
+
693
+ # --- Calculate Port based on username (mirroring app_teachmode logic) ---
694
+ try:
695
+ if user == "altair":
696
+ calculated_port = 14000
697
+ elif user.startswith("guest") and user[5:].isdigit():
698
+ num = int(user[5:])
699
+ if 1 <= num <= 10:
700
+ calculated_port = 14000 + num
701
+ else:
702
+ self.log_warning(f"Internal trigger: User 'guest{num}' out of range (1-10), using default port {calculated_port} for launch.")
703
+ # else: use default 7888
704
+ self.log_info(f"Internal trigger: Calculated port {calculated_port} for user '{user}'.")
705
+ except Exception as port_calc_e:
706
+ self.log_error(f"Internal trigger: Error calculating port for user '{user}': {port_calc_e}. Using default port {calculated_port}.")
707
+ # --- End Port Calculation ---
708
+
691
709
 
692
710
  try:
693
- # 1. Check if user is active (required to trigger task meaningfully)
711
+ # 1. Ensure scheduled task exists (still useful fallback/persistence)
712
+ try:
713
+ task_created = self.create_or_update_logon_task(user)
714
+ task_created_status = "task_success" if task_created else "task_failed"
715
+ except Exception as task_err:
716
+ self.log_error(f"Internal trigger: Exception creating/updating task for {user}: {task_err}", exc_info=True)
717
+ task_created_status = "task_exception"
718
+ # Don't necessarily fail the whole operation yet
719
+
720
+ # 2. Check if user is active
721
+ active_sessions = {} # Re-check active sessions specifically for this user
694
722
  session_id = None
723
+ token = None
695
724
  is_active = False
696
725
  try:
697
726
  sessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
@@ -707,69 +736,89 @@ class GuardService(win32serviceutil.ServiceFramework):
707
736
  except Exception: pass # Ignore errors querying other sessions
708
737
  except Exception as e:
709
738
  self.log_error(f"Internal trigger: Error checking active sessions for {user}: {e}")
710
- # If we can't check, assume inactive for safety?
711
- return "failed_session_check" # Exit if we cannot verify active state
712
-
739
+ # Continue, assume inactive if check failed?
740
+
713
741
  if not is_active:
714
- self.log_info(f"Internal trigger: User '{user}' is not active. Skipping immediate start via task.")
715
- # Still ensure task exists for future logins
716
- try:
717
- task_created = self.create_or_update_logon_task(user)
718
- if not task_created:
719
- self.log_error(f"Failed to create/update logon task for inactive user {user}")
720
- # Maybe return a specific status?
721
- except Exception as task_err:
722
- self.log_error(f"Exception creating/updating task for inactive user {user}: {task_err}")
723
- return "skipped_inactive_user"
724
-
725
- # 2. Check if already running for this active user
742
+ self.log_info(f"Internal trigger: User '{user}' is not active. Skipping immediate start.")
743
+ immediate_start_status = "start_skipped_inactive"
744
+ final_status = task_created_status # Status depends only on task creation
745
+ return final_status # Exit early if inactive
746
+
747
+ # 3. Check if already running for this active user
726
748
  is_running = False
727
749
  try:
728
750
  running_procs = self._get_ootb_processes(user)
729
751
  if running_procs:
730
752
  is_running = True
731
- self.log_info(f"Internal trigger: OOTB already running for active user '{user}'. Skipping task start.")
732
- final_status = "success_already_running"
753
+ self.log_info(f"Internal trigger: OOTB already running for active user '{user}'. Skipping immediate start.")
754
+ immediate_start_status = "start_skipped_already_running"
755
+ final_status = "success_already_running" # Considered success
733
756
  return final_status # Exit early if already running
734
757
  except Exception as e:
735
758
  self.log_error(f"Internal trigger: Error checking existing processes for {user}: {e}")
736
- # Proceed with start attempt despite error?
759
+ # Continue and attempt start despite error?
737
760
 
738
- # 3. Ensure the scheduled task exists and is configured correctly (redundant but safe)
739
- self.log_info(f"Internal trigger: Ensuring elevated task '{task_name}' exists for user '{user}'...")
740
- try:
741
- task_created = self.create_or_update_logon_task(user)
742
- if not task_created:
743
- self.log_error(f"Internal trigger: Failed to create/update required task '{task_name}'. Cannot start.")
744
- return "failed_task_creation"
745
- except Exception as task_err:
746
- self.log_error(f"Internal trigger: Exception creating/updating task '{task_name}': {task_err}", exc_info=True)
747
- return "failed_task_exception"
761
+ # 4. Attempt immediate start (User is active and not running)
762
+ immediate_start_status = "start_attempted"
763
+ self.log_info(f"Internal trigger: User '{user}' is active and not running. Attempting immediate start via PowerShell elevation...")
764
+
765
+ if not self.target_executable_path:
766
+ self.log_error("Internal trigger: Cannot start process - target executable path not found.")
767
+ final_status = "failed_exe_not_found"
768
+ return final_status # Exit early if executable missing
748
769
 
749
- # 4. Attempt immediate start by running the scheduled task
750
- self.log_info(f"Internal trigger: User '{user}' is active and OOTB not running. Attempting start via Start-ScheduledTask '{task_name}'...")
751
- ps_command = f'Start-ScheduledTask -TaskName "{task_name}"'
752
770
  try:
753
- success = self.run_powershell_command(ps_command)
754
- if success:
755
- self.log_info(f"Internal trigger: Successfully executed Start-ScheduledTask for '{task_name}'.")
756
- # We assume success here; actual process start confirmation is difficult async
757
- final_status = "success_task_started"
758
- # Check process again after a delay?
759
- time.sleep(3) # Give it a moment to start
760
- if self._get_ootb_processes(user):
761
- self.log_info(f"Internal trigger: Confirmed OOTB process running for user '{user}' after task start.")
762
- else:
763
- self.log_warning(f"Internal trigger: OOTB process NOT detected for user '{user}' shortly after task start. Task might have failed internally.")
764
- final_status = "warning_start_task_no_process_detected"
765
- else:
766
- self.log_error(f"Internal trigger: Failed to execute Start-ScheduledTask for '{task_name}'. See PowerShell logs.")
767
- final_status = "failed_start_task_command"
771
+ token = win32ts.WTSQueryUserToken(session_id)
772
+ env = win32profile.CreateEnvironmentBlock(token, False)
773
+ startup = win32process.STARTUPINFO()
774
+ creation_flags = win32con.CREATE_NO_WINDOW # Hide intermediate powershell/cmd window if possible
775
+
776
+ # Command to run PowerShell, which then elevates the target executable
777
+ target_exe_for_ps = self.target_executable_path.strip('"').replace("'", "''") # Path for PS string, escape single quotes
778
+ arguments_for_ps = f"--port {calculated_port} --target_user '{user}'" # Pass calculated port and target user
779
+ powershell_command = (
780
+ f'Start-Process -FilePath "{target_exe_for_ps}" '
781
+ f'-ArgumentList "{arguments_for_ps}" -Verb RunAs'
782
+ )
783
+ # Escape the command for cmd.exe /c
784
+ escaped_ps_command = powershell_command.replace('"', '\\"')
785
+
786
+ # Use cmd /c to launch powershell - this seems more reliable with CreateProcessAsUser sometimes
787
+ # lpCommandLine = f'powershell.exe -ExecutionPolicy Bypass -NoProfile -Command "{powershell_command}"'
788
+ lpCommandLine = f'cmd.exe /c "powershell.exe -ExecutionPolicy Bypass -NoProfile -Command \\"{escaped_ps_command}\\""'
768
789
 
769
- except Exception as start_task_err:
770
- self.log_error(f"Internal trigger: Exception running Start-ScheduledTask for '{task_name}': {start_task_err}", exc_info=True)
771
- final_status = "failed_start_task_exception"
790
+ lpApplicationName = None # Must be None if using lpCommandLine
791
+ cwd = os.path.dirname(target_exe_for_ps) if os.path.dirname(target_exe_for_ps) else None
772
792
 
793
+ # Log details before call
794
+ self.log_info(f"Internal trigger: Calling CreateProcessAsUser:")
795
+ self.log_info(f" lpCommandLine: {lpCommandLine}")
796
+ self.log_info(f" lpCurrentDirectory: {cwd if cwd else 'Default'}")
797
+
798
+ hProcess, hThread, dwPid, dwTid = win32process.CreateProcessAsUser(
799
+ token, lpApplicationName, lpCommandLine, None, None, False,
800
+ creation_flags, env, cwd, startup
801
+ )
802
+ # We get the PID of cmd.exe/powershell.exe here, not the final elevated process.
803
+ # We can't easily track the elevated process PID across the UAC boundary.
804
+ self.log_info(f"Internal trigger: CreateProcessAsUser call to initiate elevation succeeded for user '{user}' (Launcher PID: {dwPid}). UAC prompt expected if needed.")
805
+ win32api.CloseHandle(hProcess)
806
+ win32api.CloseHandle(hThread)
807
+
808
+ # Assume success if the call didn't raise an exception.
809
+ # We cannot easily verify if the *elevated* process actually started.
810
+ immediate_start_status = "start_elevation_initiated"
811
+ final_status = "success_elevation_initiated" # Report success based on initiating the elevation
812
+
813
+ except Exception as proc_err:
814
+ self.log_error(f"Internal trigger: Exception during CreateProcessAsUser for elevation attempt (user '{user}'): {proc_err}", exc_info=True)
815
+ immediate_start_status = "start_failed_exception"
816
+ final_status = "failed_start_exception"
817
+ finally:
818
+ if token: win32api.CloseHandle(token)
819
+
820
+ # Combine results (mostly determined by start attempt now)
821
+ # Example: final_status = f"{task_created_status}_{immediate_start_status}"
773
822
  return final_status
774
823
 
775
824
  except Exception as e:
@@ -792,32 +841,32 @@ class GuardService(win32serviceutil.ServiceFramework):
792
841
  python_exe = f'"{python_exe}"'
793
842
 
794
843
  task_name = f"OOTB_UserConnect_{username}"
795
- # Calculate the port for this user
796
- calculated_port = self._calculate_port_for_user(username)
797
-
798
- # Action: Run the target executable directly, passing the calculated port
799
- action_executable = self.target_executable_path
800
- action_arguments = f'--port {calculated_port}' # Use --port or -p
844
+ # Action: Run python.exe with the signal script and username argument
845
+ action_executable = python_exe
846
+ # Ensure script path is quoted if needed
847
+ script_arg = self.signal_script_path # Should be quoted already by _find_signal_script
848
+ # Username might need quoting if it contains spaces, though unlikely
849
+ user_arg = username # Keep simple for now
850
+ action_arguments = f'{script_arg} "{user_arg}"' # Pass username as quoted arg
801
851
  safe_action_executable = action_executable.replace("'", "''") # Escape for PS
802
852
  safe_action_arguments = action_arguments.replace("'", "''") # Escape for PS
803
853
 
804
- # Working directory for the executable (its own directory)
854
+ # Working directory for the script (likely its own directory)
805
855
  try:
806
- exe_dir = os.path.dirname(self.target_executable_path.strip('"'))
807
- if not exe_dir: exe_dir = "."
808
- safe_working_directory = exe_dir.replace("'", "''")
856
+ script_dir = os.path.dirname(self.signal_script_path.strip('"'))
857
+ if not script_dir: script_dir = "."
858
+ safe_working_directory = script_dir.replace("'", "''")
809
859
  working_directory_setting = f"$action.WorkingDirectory = '{safe_working_directory}'"
810
860
  except Exception as e:
811
- self.log_error(f"Error determining working directory for OOTB executable task: {e}. WD will not be set.")
861
+ self.log_error(f"Error determining working directory for signal script task: {e}. WD will not be set.")
812
862
  working_directory_setting = "# Could not set WorkingDirectory"
813
863
 
814
864
  # PowerShell command construction
815
865
  ps_command = f"""
816
866
  $taskName = "{task_name}"
817
- # Run as the specified user, with Highest privileges
818
- $principal = New-ScheduledTaskPrincipal -UserId "{username}" -LogonType Interactive -RunLevel Highest
867
+ $principal = New-ScheduledTaskPrincipal -UserId "{username}" -LogonType Interactive
819
868
 
820
- # Action: Run the OOTB executable directly with args
869
+ # Action: Run python signal script
821
870
  $action = New-ScheduledTaskAction -Execute '{safe_action_executable}' -Argument '{safe_action_arguments}'
822
871
  {working_directory_setting}
823
872
 
@@ -889,9 +938,7 @@ class GuardService(win32serviceutil.ServiceFramework):
889
938
  return True
890
939
 
891
940
  def _find_signal_script(self):
892
- """Finds the signal_connection.py script relative to this service file. DEPRECATED?"""
893
- # This might no longer be needed if the task directly runs the main executable
894
- self.log_warning("_find_signal_script called - this might be deprecated.")
941
+ """Finds the signal_connection.py script relative to this service file."""
895
942
  try:
896
943
  base_dir = os.path.dirname(os.path.abspath(__file__))
897
944
  script_path = os.path.join(base_dir, "signal_connection.py")
@@ -908,30 +955,6 @@ class GuardService(win32serviceutil.ServiceFramework):
908
955
  self.log_error(f"Error finding signal script: {e}")
909
956
  return None
910
957
 
911
- def _calculate_port_for_user(self, username):
912
- """Calculates the target port based on the username."""
913
- port = _DEFAULT_OOTB_PORT # Start with default
914
- username_lower = username.lower()
915
- self.log_info(f"Calculating port for username: {username_lower}")
916
- try:
917
- if username_lower == "altair":
918
- port = 14000
919
- elif username_lower.startswith("guest") and username_lower[5:].isdigit():
920
- num = int(username_lower[5:])
921
- if 1 <= num <= 10:
922
- port = 14000 + num
923
- else:
924
- self.log_warning(f"Guest user number {num} out of range (1-10) for port calculation, using default {port}.")
925
- # Add Administrator or other specific users if needed
926
- # elif username_lower == "administrator":
927
- # port = 14999 # Example
928
- else:
929
- self.log_info(f"Username '{username_lower}' doesn't match specific rules, using default port {port}.")
930
- except Exception as e:
931
- self.log_error(f"Error during port calculation for {username_lower}: {e}. Using default {port}.", exc_info=True)
932
- self.log_info(f"Calculated port {port} for user {username_lower}")
933
- return port
934
-
935
958
  # --- Main Execution Block ---
936
959
  if __name__ == '__main__':
937
960
  if len(sys.argv) > 1 and sys.argv[1] == 'debug':