accli 2.0.2__tar.gz → 2.0.4__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 (27) hide show
  1. {accli-2.0.2 → accli-2.0.4}/PKG-INFO +3 -3
  2. {accli-2.0.2 → accli-2.0.4}/README.md +2 -2
  3. {accli-2.0.2 → accli-2.0.4}/accli/_version.py +1 -1
  4. {accli-2.0.2 → accli-2.0.4}/accli/cleanup-nfs.ps1 +7 -1
  5. {accli-2.0.2 → accli-2.0.4}/accli/cli.py +89 -3
  6. accli-2.0.4/accli/refresh_explorer.ps1 +12 -0
  7. {accli-2.0.2 → accli-2.0.4}/accli/setup-nfs.ps1 +33 -1
  8. accli-2.0.4/accli/windows_refresher.py +159 -0
  9. {accli-2.0.2 → accli-2.0.4}/accli.egg-info/PKG-INFO +3 -3
  10. {accli-2.0.2 → accli-2.0.4}/accli.egg-info/SOURCES.txt +2 -0
  11. {accli-2.0.2 → accli-2.0.4}/LICENSE +0 -0
  12. {accli-2.0.2 → accli-2.0.4}/accli/AcceleratorJobProjectService.py +0 -0
  13. {accli-2.0.2 → accli-2.0.4}/accli/AcceleratorTaskDispatcher.py +0 -0
  14. {accli-2.0.2 → accli-2.0.4}/accli/AcceleratorTerminalCliProjectService.py +0 -0
  15. {accli-2.0.2 → accli-2.0.4}/accli/CsvRegionalTimeseriesValidator.py +0 -0
  16. {accli-2.0.2 → accli-2.0.4}/accli/__init__.py +0 -0
  17. {accli-2.0.2 → accli-2.0.4}/accli/__main__.py +0 -0
  18. {accli-2.0.2 → accli-2.0.4}/accli/common.py +0 -0
  19. {accli-2.0.2 → accli-2.0.4}/accli/gui.py +0 -0
  20. {accli-2.0.2 → accli-2.0.4}/accli/mount_downloader.py +0 -0
  21. {accli-2.0.2 → accli-2.0.4}/accli/token.py +0 -0
  22. {accli-2.0.2 → accli-2.0.4}/accli.egg-info/dependency_links.txt +0 -0
  23. {accli-2.0.2 → accli-2.0.4}/accli.egg-info/entry_points.txt +0 -0
  24. {accli-2.0.2 → accli-2.0.4}/accli.egg-info/requires.txt +0 -0
  25. {accli-2.0.2 → accli-2.0.4}/accli.egg-info/top_level.txt +0 -0
  26. {accli-2.0.2 → accli-2.0.4}/pyproject.toml +0 -0
  27. {accli-2.0.2 → accli-2.0.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: accli
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: IIASA Accelerator Client
5
5
  Author-email: Wrufesh S <wrufesh@gmail.com>
6
6
  License: The MIT License (MIT)
@@ -141,10 +141,10 @@ Usage: accli [OPTIONS] COMMAND [ARGS]...
141
141
  1. Copy and paste certificates from `<control_services_backend>/certs/ca.crt` as
142
142
  `./certs/dev-backend.crt` and `<accelerator_service>/minio_certs/ca.crt` as
143
143
  `./certs/dev-minio-ca.crt`.
144
- 2. Run
144
+ 2. Run as Administrator
145
145
  ```
146
146
  cd scripts
147
- import_certs_certutil.ps1
147
+ .\import_certs_certutil.ps1
148
148
  ```
149
149
 
150
150
  ### 🛠 Build & Upload
@@ -97,10 +97,10 @@ Usage: accli [OPTIONS] COMMAND [ARGS]...
97
97
  1. Copy and paste certificates from `<control_services_backend>/certs/ca.crt` as
98
98
  `./certs/dev-backend.crt` and `<accelerator_service>/minio_certs/ca.crt` as
99
99
  `./certs/dev-minio-ca.crt`.
100
- 2. Run
100
+ 2. Run as Administrator
101
101
  ```
102
102
  cd scripts
103
- import_certs_certutil.ps1
103
+ .\import_certs_certutil.ps1
104
104
  ```
105
105
 
106
106
  ### 🛠 Build & Upload
@@ -1,3 +1,3 @@
1
1
  # Please strictly put double quote to use this info for git tag
2
2
  # Read a developer guide for git tag command with regex
3
- VERSION = "v2.0.2"
3
+ VERSION = "v2.0.4"
@@ -34,10 +34,16 @@ if (Get-Command Unregister-ScheduledTask -ErrorAction SilentlyContinue) {
34
34
  Write-Host "[OK] Scheduled Tasks removed." -ForegroundColor Green
35
35
 
36
36
  # 2. Delete Registry Policy Keys
37
- Write-Host "Removing registry policies (EnableLinkedConnections, AnonymousUid, AnonymousGid)..." -ForegroundColor Yellow
37
+ Write-Host "Removing registry policies (EnableLinkedConnections, AnonymousUid, AnonymousGid, Cache)..." -ForegroundColor Yellow
38
38
  Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableLinkedConnections" -ErrorAction SilentlyContinue | Out-Null
39
39
  Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" -Name "AnonymousUid" -ErrorAction SilentlyContinue | Out-Null
40
40
  Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" -Name "AnonymousGid" -ErrorAction SilentlyContinue | Out-Null
41
+ Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" -Name "DisableCache" -ErrorAction SilentlyContinue | Out-Null
42
+ Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" -Name "MaxDirCacheSize" -ErrorAction SilentlyContinue | Out-Null
43
+ Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Users\Default\Cache" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
44
+ Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Users" -ErrorAction SilentlyContinue |
45
+ Where-Object { $_.PSChildName -like "S-1-5-*" } |
46
+ ForEach-Object { Remove-Item -Path "$($_.PSPath)\Cache" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null }
41
47
  Write-Host "[OK] Registry policies removed." -ForegroundColor Green
42
48
 
43
49
  # 3. Delete Config Folder & Helpers
@@ -604,6 +604,76 @@ def find_available_windows_drive(preferred: str = "W") -> str:
604
604
  raise RuntimeError("No available Windows drive letters found.")
605
605
 
606
606
 
607
+ def start_windows_explorer_refresher(mount_point: str, interval: int):
608
+ """Starts a lightweight background watcher in the user session that notifies Explorer when remote changes occur."""
609
+ import sys
610
+ import subprocess
611
+ from pathlib import Path
612
+
613
+ refresher_script = Path(__file__).parent / "windows_refresher.py"
614
+ if not refresher_script.is_file():
615
+ return
616
+
617
+ # Locate Python interpreter (prefer pythonw to run completely windowless if available)
618
+ py_exec = sys.executable
619
+ parent_dir = Path(py_exec).parent
620
+ pythonw = parent_dir / "pythonw.exe"
621
+ if pythonw.is_file():
622
+ executable = str(pythonw)
623
+ else:
624
+ executable = py_exec
625
+
626
+ try:
627
+ # DETACHED_PROCESS (0x00000008) | CREATE_NEW_PROCESS_GROUP (0x00000200) | CREATE_NO_WINDOW (0x08000000)
628
+ creationflags = 0x08000008 | 0x00000200
629
+ proc = subprocess.Popen(
630
+ [executable, str(refresher_script), str(mount_point), str(interval)],
631
+ creationflags=creationflags,
632
+ stdout=subprocess.DEVNULL,
633
+ stderr=subprocess.DEVNULL,
634
+ close_fds=True
635
+ )
636
+ pid_file = Path("C:/ProgramData/accli/refresher.pid")
637
+ try:
638
+ pid_file.parent.mkdir(parents=True, exist_ok=True)
639
+ pid_file.write_text(str(proc.pid), encoding="utf-8")
640
+ except Exception:
641
+ pass
642
+ except Exception:
643
+ pass
644
+
645
+
646
+ def stop_windows_explorer_refresher():
647
+ """Stops any active background Explorer watcher process."""
648
+ import subprocess
649
+ from pathlib import Path
650
+
651
+ pid_file = Path("C:/ProgramData/accli/refresher.pid")
652
+ if pid_file.is_file():
653
+ try:
654
+ pid_str = pid_file.read_text(encoding="utf-8").strip()
655
+ if pid_str:
656
+ subprocess.run(["taskkill", "/F", "/PID", pid_str], capture_output=True)
657
+ except Exception:
658
+ pass
659
+ try:
660
+ pid_file.unlink()
661
+ except Exception:
662
+ pass
663
+
664
+ # Also clean up any lingering windows_refresher.py processes
665
+ try:
666
+ cleanup_cmd = [
667
+ "powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command",
668
+ "Get-CimInstance Win32_Process -Filter \"name='python.exe' or name='pythonw.exe'\" | "
669
+ "Where-Object { $_.CommandLine -like '*windows_refresher.py*' } | "
670
+ "ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }"
671
+ ]
672
+ subprocess.run(cleanup_cmd, capture_output=True)
673
+ except Exception:
674
+ pass
675
+
676
+
607
677
  def enable_windows_nfs_features():
608
678
  """Automatically check and enable Windows Client for NFS features, registry key, and Task Scheduler gateway if needed."""
609
679
  import subprocess
@@ -631,6 +701,13 @@ def enable_windows_nfs_features():
631
701
  " if ($null -ne $valUid -and $valUid.AnonymousUid -eq 0 -and $null -ne $valGid -and $valGid.AnonymousGid -eq 0) { $anonEnabled = $true }\n"
632
702
  "}\n"
633
703
  "\n"
704
+ "$cacheEnabled = $false\n"
705
+ "$cachePath = 'HKLM:\\SOFTWARE\\Microsoft\\ClientForNFS\\CurrentVersion\\Users\\Default\\Cache'\n"
706
+ "if (Test-Path $cachePath) {\n"
707
+ " $valDelta = Get-ItemProperty -Path $cachePath -Name 'AttributeTimeDelta' -ErrorAction SilentlyContinue\n"
708
+ " if ($null -ne $valDelta -and $valDelta.AttributeTimeDelta -ge 1) { $cacheEnabled = $true }\n"
709
+ "}\n"
710
+ "\n"
634
711
  "$taskEnabled = $false\n"
635
712
  "if (Get-Command Get-ScheduledTask -ErrorAction SilentlyContinue) {\n"
636
713
  " $t = Get-ScheduledTask -TaskName 'accli-mount-nfs' -ErrorAction SilentlyContinue\n"
@@ -640,7 +717,7 @@ def enable_windows_nfs_features():
640
717
  " if ($LASTEXITCODE -eq 0) { $taskEnabled = $true }\n"
641
718
  "}\n"
642
719
  "\n"
643
- "Write-Output \"NFS:$nfsEnabled;REG:$regEnabled;ANON:$anonEnabled;TASK:$taskEnabled\""
720
+ "Write-Output \"NFS:$nfsEnabled;REG:$regEnabled;ANON:$anonEnabled;CACHE:$cacheEnabled;TASK:$taskEnabled\""
644
721
  )
645
722
 
646
723
  check_cmd = [
@@ -651,6 +728,7 @@ def enable_windows_nfs_features():
651
728
  nfs_ok = False
652
729
  reg_ok = False
653
730
  anon_ok = False
731
+ cache_ok = False
654
732
  task_ok = False
655
733
 
656
734
  try:
@@ -663,13 +741,15 @@ def enable_windows_nfs_features():
663
741
  reg_ok = (part.split(':')[1].lower() == 'true')
664
742
  elif part.startswith("ANON:"):
665
743
  anon_ok = (part.split(':')[1].lower() == 'true')
744
+ elif part.startswith("CACHE:"):
745
+ cache_ok = (part.split(':')[1].lower() == 'true')
666
746
  elif part.startswith("TASK:"):
667
747
  task_ok = (part.split(':')[1].lower() == 'true')
668
748
  except Exception as e:
669
749
  print(f"[yellow]Warning: Could not audit current Windows NFS settings: {e}[/yellow]")
670
750
 
671
- # Require both general registry settings and Anonymous mappings to be correct
672
- reg_ok = reg_ok and anon_ok
751
+ # Require general registry settings, Anonymous mappings, and Cache policies to be correct
752
+ reg_ok = reg_ok and anon_ok and cache_ok
673
753
 
674
754
  if nfs_ok and reg_ok and task_ok:
675
755
  print("[bold green][OK] Windows NFS client features, registry policies, and Task Scheduler gateway are already configured.[/bold green]")
@@ -986,6 +1066,7 @@ def mount_start(
986
1066
  ]
987
1067
  mount_res = subprocess.run(mount_cmd, capture_output=True, text=True)
988
1068
  if mount_res.returncode == 0:
1069
+ start_windows_explorer_refresher(str(mount_point_abs), interval=30)
989
1070
  print(f"[bold green][OK] NFS mount successfully mapped at [white]{mount_point_abs}[/white]![/bold green]")
990
1071
  print("[cyan]Use 'umount' or 'accli mount stop' to unmount the drive.[/cyan]")
991
1072
  return
@@ -1039,6 +1120,7 @@ def mount_start(
1039
1120
  print(f"[yellow]Please check the log file at {log_file_path} for more details.[/yellow]")
1040
1121
  raise typer.Exit(1)
1041
1122
 
1123
+ start_windows_explorer_refresher(str(mount_point_abs), interval=30)
1042
1124
  print(f"[bold green][OK] NFS mount process spawned successfully (PID: {process.pid}).[/bold green]")
1043
1125
  print(f"[cyan]Use 'umount' or 'accli mount stop' to unmount the drive.[/cyan]")
1044
1126
  else:
@@ -1086,6 +1168,7 @@ def mount_start(
1086
1168
  ]
1087
1169
  mount_res = subprocess.run(mount_cmd, capture_output=True, text=True)
1088
1170
  if mount_res.returncode == 0:
1171
+ start_windows_explorer_refresher(str(mount_point_abs), interval=30)
1089
1172
  print(f"[bold green][OK] NFS mount successfully mapped at [white]{mount_point_abs}[/white]![/bold green]")
1090
1173
  print("[cyan]Use 'umount' or 'accli mount stop' to unmount the drive.[/cyan]")
1091
1174
  else:
@@ -1223,6 +1306,9 @@ def mount_stop(
1223
1306
  except Exception:
1224
1307
  is_admin = False
1225
1308
 
1309
+ # 0. Stop Explorer background refresher
1310
+ stop_windows_explorer_refresher()
1311
+
1226
1312
  # 1. Unmap the network drive in the user session
1227
1313
  res = subprocess.run(["C:\\Windows\\System32\\umount.exe", "-f", str(mount_point_abs)], capture_output=True, text=True)
1228
1314
  if res.returncode != 0:
@@ -0,0 +1,12 @@
1
+ $ErrorActionPreference = "SilentlyContinue"
2
+ $mountPoint = $args[0]
3
+ if (-not $mountPoint) { $mountPoint = "W:" }
4
+ $mountClean = $mountPoint.TrimEnd("\/")
5
+
6
+ $shell = New-Object -ComObject Shell.Application
7
+ foreach ($win in $shell.Windows()) {
8
+ $url = $win.LocationURL
9
+ if ($url -and ($url -like "*$mountClean*" -or $win.Path -like "*$mountClean*")) {
10
+ $win.Refresh()
11
+ }
12
+ }
@@ -75,6 +75,39 @@ if (-not $RegAlreadyEnabled) {
75
75
  Write-Host "[OK] Anonymous UID/GID registry values already configured (skipped)." -ForegroundColor Green
76
76
  }
77
77
 
78
+ # 2.6 Configure Client for NFS Cache (fast directory refresh without caching delay)
79
+ Write-Host "Configuring Client for NFS directory cache timeouts..." -ForegroundColor Cyan
80
+
81
+ # Global redirector controls:
82
+ # DisableCache = 0 allows file content caching in memory
83
+ New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" -Name "DisableCache" -Value 0 -PropertyType DWORD -Force | Out-Null
84
+ # MaxDirCacheSize = 0 bypasses directory tree RAM cache so newly created/deleted files appear immediately
85
+ New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default" -Name "MaxDirCacheSize" -Value 0 -PropertyType DWORD -Force | Out-Null
86
+
87
+ $cacheTargets = @(
88
+ "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Users\Default\Cache"
89
+ )
90
+
91
+ $existingUsers = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Users" -ErrorAction SilentlyContinue |
92
+ Where-Object { $_.PSChildName -like "S-1-5-*" }
93
+
94
+ foreach ($u in $existingUsers) {
95
+ $cacheTargets += "$($u.PSPath)\Cache"
96
+ }
97
+
98
+ foreach ($targetPath in $cacheTargets) {
99
+ if (-not (Test-Path $targetPath)) {
100
+ New-Item -Path $targetPath -Force | Out-Null
101
+ }
102
+ # Enable the attribute cache
103
+ New-ItemProperty -Path $targetPath -Name "FileAttributeCache" -Value 1 -PropertyType DWORD -Force | Out-Null
104
+ # AttributeTimeDelta: metadata TTL (10s) to align with --metadata-ttl-ms 10000
105
+ New-ItemProperty -Path $targetPath -Name "AttributeTimeDelta" -Value 10 -PropertyType DWORD -Force | Out-Null
106
+ # CacheRefreshInterval: file content cache TTL (10s) so modified files refresh within 10s
107
+ New-ItemProperty -Path $targetPath -Name "CacheRefreshInterval" -Value 10 -PropertyType DWORD -Force | Out-Null
108
+ }
109
+ Write-Host "[OK] Client for NFS balanced cache policy configured (10s TTL, No Dir Cache)." -ForegroundColor Green
110
+
78
111
  # 3. Create public ProgramData folder and configure permissions for Authenticated Users
79
112
  Write-Host "Initializing public accli configuration directory..." -ForegroundColor Cyan
80
113
  $configDir = "C:\ProgramData\accli"
@@ -126,7 +159,6 @@ try {
126
159
  }
127
160
  } catch {}
128
161
 
129
- # Construct arguments
130
162
  $mountArgs = @(
131
163
  "--token-file", $cfg.db_path,
132
164
  "--hub-endpoint", $cfg.server_url,
@@ -0,0 +1,159 @@
1
+ import os
2
+ import sys
3
+ import time
4
+ import ctypes
5
+ import subprocess
6
+ from pathlib import Path
7
+ from datetime import datetime
8
+
9
+ # Windows Shell Notification constants
10
+ SHCNE_UPDATEDIR = 0x00001000
11
+ SHCNF_PATHW = 0x0005
12
+
13
+
14
+ def log(msg: str):
15
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
16
+ print(f"[{timestamp}] [Watcher] {msg}", flush=True)
17
+
18
+
19
+ def notify_explorer(path_str: str):
20
+ """Notifies Windows Shell / Explorer to refresh views for path_str."""
21
+ log(f"Broadcasting SHChangeNotify on '{path_str}'...")
22
+ try:
23
+ ctypes.windll.shell32.SHChangeNotify(
24
+ SHCNE_UPDATEDIR,
25
+ SHCNF_PATHW,
26
+ path_str,
27
+ None
28
+ )
29
+ except Exception as e:
30
+ log(f"SHChangeNotify error: {e}")
31
+
32
+ try:
33
+ script_path = Path(__file__).parent / "refresh_explorer.ps1"
34
+ if script_path.is_file():
35
+ log(f"Executing COM window refresh for '{path_str}'...")
36
+ res = subprocess.run(
37
+ [
38
+ "powershell.exe", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
39
+ "-File", str(script_path), path_str
40
+ ],
41
+ capture_output=True,
42
+ text=True,
43
+ creationflags=0x08000000 # CREATE_NO_WINDOW
44
+ )
45
+ if res.stdout and res.stdout.strip():
46
+ log(f"COM output: {res.stdout.strip()}")
47
+ if res.stderr and res.stderr.strip():
48
+ log(f"COM error: {res.stderr.strip()}")
49
+ log("Explorer windows refreshed.")
50
+ except Exception as e:
51
+ log(f"COM refresh error: {e}")
52
+
53
+
54
+ def scan_directory_state(root_path: Path):
55
+ """
56
+ Fast snapshot of directory state: mapping of relative_path -> (size, mtime_ns).
57
+ Returns None if root_path is not accessible or unmounted.
58
+ """
59
+ state = {}
60
+ try:
61
+ if not root_path.exists():
62
+ return None
63
+
64
+ for root, dirs, files in os.walk(root_path):
65
+ for name in files:
66
+ full_path = os.path.join(root, name)
67
+ try:
68
+ st = os.stat(full_path)
69
+ rel = os.path.relpath(full_path, root_path)
70
+ state[rel] = (st.st_size, st.st_mtime_ns)
71
+ except (OSError, PermissionError):
72
+ continue
73
+ for name in dirs:
74
+ full_path = os.path.join(root, name)
75
+ try:
76
+ st = os.stat(full_path)
77
+ rel = os.path.relpath(full_path, root_path)
78
+ state[rel] = (-1, st.st_mtime_ns)
79
+ except (OSError, PermissionError):
80
+ continue
81
+ return state
82
+ except Exception:
83
+ return None
84
+
85
+
86
+ def describe_changes(old_state, new_state):
87
+ old_keys = set(old_state.keys())
88
+ new_keys = set(new_state.keys())
89
+ added = new_keys - old_keys
90
+ removed = old_keys - new_keys
91
+ modified = {k for k in (old_keys & new_keys) if old_state[k] != new_state[k]}
92
+
93
+ parts = []
94
+ if added:
95
+ parts.append(f"Added: {list(added)[:5]}")
96
+ if removed:
97
+ parts.append(f"Removed: {list(removed)[:5]}")
98
+ if modified:
99
+ parts.append(f"Modified: {list(modified)[:5]}")
100
+ return "; ".join(parts)
101
+
102
+
103
+ def run_watcher(mount_point: str, interval: float):
104
+ """
105
+ Periodically inspects mount_point for remote additions/deletions/modifications.
106
+ Triggers Windows Shell & open Explorer window refreshes whenever changes are detected.
107
+ """
108
+ drive_root = mount_point.rstrip("\\/") + "\\"
109
+ root_path = Path(drive_root)
110
+
111
+ log(f"Starting watcher on '{drive_root}' (poll interval: {interval}s)...")
112
+
113
+ # Baseline snapshot
114
+ try:
115
+ last_state = scan_directory_state(root_path)
116
+ except Exception as e:
117
+ last_state = None
118
+ log(f"Initial scan error: {e}")
119
+
120
+ if last_state is not None:
121
+ log(f"Initial baseline snapshot indexed {len(last_state)} items on '{drive_root}'.")
122
+ else:
123
+ log(f"Warning: Could not read '{drive_root}'. Waiting for drive to become available...")
124
+
125
+ while True:
126
+ try:
127
+ time.sleep(interval)
128
+ except (KeyboardInterrupt, SystemExit):
129
+ log("Watcher stopped.")
130
+ break
131
+ except Exception as e:
132
+ log(f"Sleep interrupted: {e}")
133
+ continue
134
+
135
+ try:
136
+ current_state = scan_directory_state(root_path)
137
+ if current_state is None:
138
+ continue
139
+
140
+ if last_state is not None:
141
+ if current_state != last_state:
142
+ diff_summary = describe_changes(last_state, current_state)
143
+ log(f"Change detected on '{drive_root}' -> {diff_summary}")
144
+ notify_explorer(drive_root)
145
+ last_state = current_state
146
+ else:
147
+ last_state = current_state
148
+ log(f"Baseline established: {len(last_state)} items found.")
149
+ except Exception as e:
150
+ log(f"Loop error: {e}")
151
+
152
+
153
+ if __name__ == "__main__":
154
+ if len(sys.argv) < 3:
155
+ print("Usage: python windows_refresher.py <mount_point> <interval_seconds>")
156
+ sys.exit(1)
157
+ mp = sys.argv[1]
158
+ poll_int = float(sys.argv[2])
159
+ run_watcher(mp, poll_int)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: accli
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: IIASA Accelerator Client
5
5
  Author-email: Wrufesh S <wrufesh@gmail.com>
6
6
  License: The MIT License (MIT)
@@ -141,10 +141,10 @@ Usage: accli [OPTIONS] COMMAND [ARGS]...
141
141
  1. Copy and paste certificates from `<control_services_backend>/certs/ca.crt` as
142
142
  `./certs/dev-backend.crt` and `<accelerator_service>/minio_certs/ca.crt` as
143
143
  `./certs/dev-minio-ca.crt`.
144
- 2. Run
144
+ 2. Run as Administrator
145
145
  ```
146
146
  cd scripts
147
- import_certs_certutil.ps1
147
+ .\import_certs_certutil.ps1
148
148
  ```
149
149
 
150
150
  ### 🛠 Build & Upload
@@ -13,8 +13,10 @@ accli/cli.py
13
13
  accli/common.py
14
14
  accli/gui.py
15
15
  accli/mount_downloader.py
16
+ accli/refresh_explorer.ps1
16
17
  accli/setup-nfs.ps1
17
18
  accli/token.py
19
+ accli/windows_refresher.py
18
20
  accli.egg-info/PKG-INFO
19
21
  accli.egg-info/SOURCES.txt
20
22
  accli.egg-info/dependency_links.txt
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes