@arcforgelabs/dictate 2026.6.3

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.
@@ -0,0 +1,422 @@
1
+ param(
2
+ [switch]$NoVerify,
3
+ [switch]$NoPrepareTurbo,
4
+ [switch]$NoShortcut,
5
+ [switch]$NoStartup,
6
+ [switch]$RecreateVenv
7
+ )
8
+
9
+ $ErrorActionPreference = "Stop"
10
+
11
+ function Test-PythonVersion {
12
+ param(
13
+ [string]$Exe,
14
+ [string[]]$ArgumentList
15
+ )
16
+
17
+ & $Exe @ArgumentList -c "import sys; raise SystemExit(0 if (3, 11) <= sys.version_info < (3, 13) else 1)" *> $null
18
+ return ($LASTEXITCODE -eq 0)
19
+ }
20
+
21
+ function Resolve-Python {
22
+ $candidates = @()
23
+
24
+ if ($env:PYTHON) {
25
+ $candidates += [pscustomobject]@{ Exe = $env:PYTHON; ArgumentList = @() }
26
+ }
27
+
28
+ $python = Get-Command python -ErrorAction SilentlyContinue
29
+ if ($python) {
30
+ $candidates += [pscustomobject]@{ Exe = $python.Source; ArgumentList = @() }
31
+ }
32
+
33
+ $python3 = Get-Command python3 -ErrorAction SilentlyContinue
34
+ if ($python3) {
35
+ $candidates += [pscustomobject]@{ Exe = $python3.Source; ArgumentList = @() }
36
+ }
37
+
38
+ $py = Get-Command py -ErrorAction SilentlyContinue
39
+ if ($py) {
40
+ $candidates += [pscustomobject]@{ Exe = $py.Source; ArgumentList = @("-3.12") }
41
+ $candidates += [pscustomobject]@{ Exe = $py.Source; ArgumentList = @("-3.11") }
42
+ }
43
+
44
+ foreach ($candidate in $candidates) {
45
+ if (Test-PythonVersion -Exe $candidate.Exe -ArgumentList $candidate.ArgumentList) {
46
+ return $candidate
47
+ }
48
+ }
49
+
50
+ throw "Python 3.11 or 3.12 was not found. Install Python from python.org or winget, then rerun this script."
51
+ }
52
+
53
+ function Invoke-Checked {
54
+ param(
55
+ [string]$Exe,
56
+ [string[]]$ArgumentList,
57
+ [string]$Description
58
+ )
59
+
60
+ Write-Host "==> $Description"
61
+ & $Exe @ArgumentList
62
+ if ($LASTEXITCODE -ne 0) {
63
+ throw "$Description failed with exit code $LASTEXITCODE."
64
+ }
65
+ }
66
+
67
+ function Test-VcRuntime {
68
+ $system32 = Join-Path $env:WINDIR "System32"
69
+ $required = @("vcruntime140.dll", "vcruntime140_1.dll", "msvcp140.dll")
70
+ foreach ($dll in $required) {
71
+ if (-not (Test-Path (Join-Path $system32 $dll))) {
72
+ return $false
73
+ }
74
+ }
75
+ return $true
76
+ }
77
+
78
+ function Ensure-VcRuntime {
79
+ if (Test-VcRuntime) {
80
+ Write-Host "==> Microsoft Visual C++ runtime already installed"
81
+ return
82
+ }
83
+
84
+ $installer = Join-Path $env:TEMP "vc_redist.x64.exe"
85
+ $url = "https://aka.ms/vs/17/release/vc_redist.x64.exe"
86
+ Write-Host "==> Installing Microsoft Visual C++ runtime"
87
+ Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $installer
88
+ $process = Start-Process -FilePath $installer -ArgumentList "/install", "/quiet", "/norestart" -Wait -PassThru
89
+ if (($process.ExitCode -ne 0) -and ($process.ExitCode -ne 3010)) {
90
+ throw "Microsoft Visual C++ runtime install failed with exit code $($process.ExitCode)."
91
+ }
92
+ }
93
+
94
+ function Get-AppDataConfigPath {
95
+ $base = $env:APPDATA
96
+ if (-not $base) {
97
+ $base = Join-Path $HOME "AppData\Roaming"
98
+ }
99
+ return Join-Path $base "dictate\config.yaml"
100
+ }
101
+
102
+ function Seed-Config {
103
+ $configPath = Get-AppDataConfigPath
104
+ if (Test-Path $configPath) {
105
+ Write-Host "==> Config already exists: $configPath"
106
+ return
107
+ }
108
+
109
+ $defaultConfig = Join-Path $PSScriptRoot "config\default-config.yaml"
110
+ $configDir = Split-Path -Parent $configPath
111
+ New-Item -ItemType Directory -Force -Path $configDir | Out-Null
112
+ Copy-Item -Path $defaultConfig -Destination $configPath
113
+ Write-Host "==> Seeded config: $configPath"
114
+ }
115
+
116
+ function Write-LauncherScripts {
117
+ param([string]$ScriptsDir)
118
+
119
+ $daemonPath = Join-Path $ScriptsDir "dictate-daemon.cmd"
120
+ $oncePath = Join-Path $ScriptsDir "dictate-once.cmd"
121
+ $controlsPath = Join-Path $ScriptsDir "dictate-controls.cmd"
122
+ $trayPath = Join-Path $ScriptsDir "dictate-tray.cmd"
123
+ $trayVbsPath = Join-Path $ScriptsDir "dictate-tray.vbs"
124
+
125
+ Set-Content -Path $trayPath -Encoding ASCII -Value @(
126
+ "@echo off",
127
+ 'set "SCRIPT_DIR=%~dp0"',
128
+ '"%SCRIPT_DIR%dictate.exe" --type-backend pynput %*'
129
+ )
130
+
131
+ Set-Content -Path $daemonPath -Encoding ASCII -Value @(
132
+ "@echo off",
133
+ 'set "SCRIPT_DIR=%~dp0"',
134
+ '"%SCRIPT_DIR%dictate.exe" --no-tray --type-backend pynput %*'
135
+ )
136
+
137
+ Set-Content -Path $oncePath -Encoding ASCII -Value @(
138
+ "@echo off",
139
+ 'set "SCRIPT_DIR=%~dp0"',
140
+ '"%SCRIPT_DIR%dictate.exe" --once %*'
141
+ )
142
+
143
+ Set-Content -Path $controlsPath -Encoding ASCII -Value @(
144
+ "@echo off",
145
+ 'set "SCRIPT_DIR=%~dp0"',
146
+ 'start "" "%SCRIPT_DIR%dictate-controls.exe" %*'
147
+ )
148
+
149
+ Set-Content -Path $trayVbsPath -Encoding ASCII -Value @(
150
+ 'Set shell = CreateObject("WScript.Shell")',
151
+ 'Set fso = CreateObject("Scripting.FileSystemObject")',
152
+ 'scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)',
153
+ 'venvDir = fso.GetParentFolderName(scriptDir)',
154
+ 'shell.CurrentDirectory = fso.GetParentFolderName(venvDir)',
155
+ 'shell.Run """" & scriptDir & "\pythonw.exe"" -m dictate --type-backend pynput", 0, False'
156
+ )
157
+
158
+ Write-Host "==> Wrote launchers:"
159
+ Write-Host " $trayPath"
160
+ Write-Host " $trayVbsPath"
161
+ Write-Host " $daemonPath"
162
+ Write-Host " $oncePath"
163
+ Write-Host " $controlsPath"
164
+ }
165
+
166
+ function Get-StartMenuProgramsDir {
167
+ $programsDir = Join-Path $env:APPDATA "Microsoft\Windows\Start Menu\Programs"
168
+ if (-not $env:APPDATA) {
169
+ $programsDir = Join-Path $HOME "AppData\Roaming\Microsoft\Windows\Start Menu\Programs"
170
+ }
171
+ return $programsDir
172
+ }
173
+
174
+ function Get-StartupDir {
175
+ return (Join-Path (Get-StartMenuProgramsDir) "Startup")
176
+ }
177
+
178
+ function New-DictateShortcut {
179
+ param(
180
+ [string]$ShortcutPath,
181
+ [string]$TargetPath,
182
+ [string]$Arguments = "",
183
+ [string]$WorkingDirectory,
184
+ [string]$Description
185
+ )
186
+
187
+ $iconPath = Join-Path $PSScriptRoot "assets\dictate.ico"
188
+ $shell = New-Object -ComObject WScript.Shell
189
+ $shortcut = $shell.CreateShortcut($ShortcutPath)
190
+ $shortcut.TargetPath = $TargetPath
191
+ $shortcut.Arguments = $Arguments
192
+ $shortcut.WorkingDirectory = $WorkingDirectory
193
+ $shortcut.Description = $Description
194
+ if (Test-Path $iconPath) {
195
+ $shortcut.IconLocation = $iconPath
196
+ }
197
+ $shortcut.Save()
198
+ }
199
+
200
+ function Install-StartMenuShortcut {
201
+ param(
202
+ [string]$TargetPath,
203
+ [string]$Arguments = "",
204
+ [string]$WorkingDirectory
205
+ )
206
+
207
+ $programsDir = Get-StartMenuProgramsDir
208
+ New-Item -ItemType Directory -Force -Path $programsDir | Out-Null
209
+
210
+ $shortcutPath = Join-Path $programsDir "Dictate.lnk"
211
+ $legacyShortcutPath = Join-Path $programsDir "Dictate Controls.lnk"
212
+
213
+ if ($NoShortcut) {
214
+ Remove-Item -Force -ErrorAction SilentlyContinue -Path $shortcutPath, $legacyShortcutPath
215
+ Write-Host "==> Removed Start Menu shortcuts"
216
+ return
217
+ }
218
+
219
+ Remove-Item -Force -ErrorAction SilentlyContinue -Path $legacyShortcutPath
220
+ New-DictateShortcut -ShortcutPath $shortcutPath -TargetPath $TargetPath -Arguments $Arguments -WorkingDirectory $WorkingDirectory -Description "Start Dictate push-to-talk tray"
221
+
222
+ Write-Host "==> Installed Start Menu shortcut: $shortcutPath"
223
+ }
224
+
225
+ function Install-StartupShortcut {
226
+ param(
227
+ [string]$TargetPath,
228
+ [string]$Arguments = "",
229
+ [string]$WorkingDirectory
230
+ )
231
+
232
+ if ($NoShortcut -or $NoStartup) {
233
+ $startupDir = Get-StartupDir
234
+ $shortcutPath = Join-Path $startupDir "Dictate.lnk"
235
+ if (Test-Path $shortcutPath) {
236
+ Remove-Item -Force $shortcutPath
237
+ Write-Host "==> Removed startup shortcut: $shortcutPath"
238
+ }
239
+ return
240
+ }
241
+
242
+ $startupDir = Get-StartupDir
243
+ New-Item -ItemType Directory -Force -Path $startupDir | Out-Null
244
+
245
+ $shortcutPath = Join-Path $startupDir "Dictate.lnk"
246
+ New-DictateShortcut -ShortcutPath $shortcutPath -TargetPath $TargetPath -Arguments $Arguments -WorkingDirectory $WorkingDirectory -Description "Start Dictate automatically at sign-in"
247
+
248
+ Write-Host "==> Installed startup shortcut: $shortcutPath"
249
+ }
250
+
251
+ function Register-InstalledApp {
252
+ param(
253
+ [string]$InstallLocation,
254
+ [string]$DisplayIcon
255
+ )
256
+
257
+ $keyPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Dictate"
258
+ New-Item -Force -Path $keyPath | Out-Null
259
+ New-ItemProperty -Force -Path $keyPath -Name "DisplayName" -Value "Dictate" -PropertyType String | Out-Null
260
+ New-ItemProperty -Force -Path $keyPath -Name "DisplayVersion" -Value "2026.6.3" -PropertyType String | Out-Null
261
+ New-ItemProperty -Force -Path $keyPath -Name "Publisher" -Value "Arc Forge Labs" -PropertyType String | Out-Null
262
+ New-ItemProperty -Force -Path $keyPath -Name "InstallLocation" -Value $InstallLocation -PropertyType String | Out-Null
263
+ if (Test-Path $DisplayIcon) {
264
+ New-ItemProperty -Force -Path $keyPath -Name "DisplayIcon" -Value $DisplayIcon -PropertyType String | Out-Null
265
+ }
266
+ $uninstallScript = Join-Path $InstallLocation "uninstall-windows.ps1"
267
+ if (Test-Path $uninstallScript) {
268
+ $uninstallCommand = "powershell -NoProfile -ExecutionPolicy Bypass -File `"$uninstallScript`""
269
+ New-ItemProperty -Force -Path $keyPath -Name "UninstallString" -Value $uninstallCommand -PropertyType String | Out-Null
270
+ New-ItemProperty -Force -Path $keyPath -Name "QuietUninstallString" -Value "$uninstallCommand -Quiet" -PropertyType String | Out-Null
271
+ }
272
+ New-ItemProperty -Force -Path $keyPath -Name "NoModify" -Value 1 -PropertyType DWord | Out-Null
273
+ New-ItemProperty -Force -Path $keyPath -Name "NoRepair" -Value 1 -PropertyType DWord | Out-Null
274
+ Write-Host "==> Registered Dictate in Windows Installed Apps"
275
+ }
276
+
277
+ function Normalize-PathForCompare {
278
+ param([string]$Path)
279
+ if (-not $Path) {
280
+ return ""
281
+ }
282
+ try {
283
+ return [System.IO.Path]::GetFullPath($Path).TrimEnd("\").ToLowerInvariant()
284
+ } catch {
285
+ return $Path.TrimEnd("\").ToLowerInvariant()
286
+ }
287
+ }
288
+
289
+ function Remove-StaleUserInstallSurface {
290
+ param([string]$CurrentInstallLocation)
291
+
292
+ $currentInstall = Normalize-PathForCompare -Path $CurrentInstallLocation
293
+ $shell = $null
294
+ try {
295
+ $shell = New-Object -ComObject WScript.Shell
296
+ } catch {
297
+ Write-Host "==> Could not inspect Windows shortcuts for stale Dictate entries"
298
+ }
299
+
300
+ $profilesRoot = Join-Path $env:SystemDrive "Users"
301
+ if (Test-Path $profilesRoot) {
302
+ Get-ChildItem -Path $profilesRoot -Directory -ErrorAction SilentlyContinue |
303
+ Where-Object { $_.Name -notin @("Public", "Default", "Default User", "All Users") } |
304
+ ForEach-Object {
305
+ $profile = $_.FullName
306
+ $programsDir = Join-Path $profile "AppData\Roaming\Microsoft\Windows\Start Menu\Programs"
307
+ $startupDir = Join-Path $programsDir "Startup"
308
+ $legacyShortcut = Join-Path $programsDir "Dictate Controls.lnk"
309
+ if (Test-Path -LiteralPath $legacyShortcut) {
310
+ Remove-Item -Force -LiteralPath $legacyShortcut -ErrorAction SilentlyContinue
311
+ Write-Host "==> Removed stale Dictate shortcut: $legacyShortcut"
312
+ }
313
+
314
+ foreach ($shortcutPath in @(
315
+ (Join-Path $programsDir "Dictate.lnk"),
316
+ (Join-Path $startupDir "Dictate.lnk")
317
+ )) {
318
+ if (-not (Test-Path -LiteralPath $shortcutPath)) {
319
+ continue
320
+ }
321
+ $removeShortcut = $false
322
+ if ($shell) {
323
+ $shortcut = $shell.CreateShortcut($shortcutPath)
324
+ $workingDirectory = Normalize-PathForCompare -Path $shortcut.WorkingDirectory
325
+ $argumentTarget = ""
326
+ if ($shortcut.Arguments -match '"([^"]+dictate-tray\.vbs)"') {
327
+ $argumentTarget = $matches[1]
328
+ }
329
+ if (
330
+ ($workingDirectory -and ($workingDirectory -ne $currentInstall)) -or
331
+ ($argumentTarget -and (-not (Test-Path -LiteralPath $argumentTarget)))
332
+ ) {
333
+ $removeShortcut = $true
334
+ }
335
+ } else {
336
+ $removeShortcut = $true
337
+ }
338
+ if ($removeShortcut) {
339
+ Remove-Item -Force -LiteralPath $shortcutPath -ErrorAction SilentlyContinue
340
+ Write-Host "==> Removed stale Dictate shortcut: $shortcutPath"
341
+ }
342
+ }
343
+
344
+ $hostedSource = Join-Path $profile "AppData\Local\Dictate\source"
345
+ $hostedRoot = Join-Path $profile "AppData\Local\Dictate"
346
+ if (
347
+ (Test-Path -LiteralPath (Join-Path $hostedSource "install-windows.ps1")) -and
348
+ ((Normalize-PathForCompare -Path $hostedSource) -ne $currentInstall)
349
+ ) {
350
+ Remove-Item -Recurse -Force -LiteralPath $hostedRoot -ErrorAction SilentlyContinue
351
+ Write-Host "==> Removed stale Dictate managed source: $hostedRoot"
352
+ }
353
+ }
354
+ }
355
+
356
+ Get-ChildItem Registry::HKEY_USERS -ErrorAction SilentlyContinue | ForEach-Object {
357
+ $keyPath = Join-Path $_.PSPath "Software\Microsoft\Windows\CurrentVersion\Uninstall\Dictate"
358
+ if (Test-Path $keyPath) {
359
+ $entry = Get-ItemProperty -Path $keyPath
360
+ if ((Normalize-PathForCompare -Path $entry.InstallLocation) -ne $currentInstall) {
361
+ Remove-Item -Recurse -Force -Path $keyPath -ErrorAction SilentlyContinue
362
+ Write-Host "==> Removed stale Dictate Installed Apps entry"
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+ $pythonCommand = Resolve-Python
369
+ $venvDir = Join-Path $PSScriptRoot ".venv"
370
+ $scriptsDir = Join-Path $venvDir "Scripts"
371
+ $venvPython = Join-Path $scriptsDir "python.exe"
372
+
373
+ Remove-StaleUserInstallSurface -CurrentInstallLocation $PSScriptRoot
374
+ Ensure-VcRuntime
375
+
376
+ if ($RecreateVenv -and (Test-Path $venvDir)) {
377
+ Write-Host "==> Removing existing virtual environment: $venvDir"
378
+ Remove-Item -Recurse -Force $venvDir
379
+ }
380
+
381
+ if (-not (Test-Path $venvPython)) {
382
+ Invoke-Checked -Exe $pythonCommand.Exe -ArgumentList @($pythonCommand.ArgumentList + @("-m", "venv", $venvDir)) -Description "Creating virtual environment"
383
+ }
384
+
385
+ Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "pip", "install", "--upgrade", "pip") -Description "Upgrading pip"
386
+ Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "pip", "install", "-e", "${PSScriptRoot}[windows]") -Description "Installing Dictate Windows package"
387
+
388
+ Seed-Config
389
+ Write-LauncherScripts -ScriptsDir $scriptsDir
390
+ $trayVbs = Join-Path $scriptsDir "dictate-tray.vbs"
391
+ $wscript = Join-Path $env:WINDIR "System32\wscript.exe"
392
+ $trayArgs = "`"$trayVbs`""
393
+ Install-StartMenuShortcut -TargetPath $wscript -Arguments $trayArgs -WorkingDirectory $PSScriptRoot
394
+ Install-StartupShortcut -TargetPath $wscript -Arguments $trayArgs -WorkingDirectory $PSScriptRoot
395
+ Register-InstalledApp -InstallLocation $PSScriptRoot -DisplayIcon (Join-Path $PSScriptRoot "assets\dictate.ico")
396
+
397
+ if (-not $NoPrepareTurbo) {
398
+ Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "dictate", "prepare-model", "--stt-backend", "faster-whisper", "--model", "turbo", "--device", "auto", "--compute-type", "int8") -Description "Preparing faster-whisper turbo model"
399
+ }
400
+
401
+ if (-not $NoVerify) {
402
+ Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "dictate", "doctor", "--quick", "--type-backend", "pynput") -Description "Running Dictate doctor"
403
+ }
404
+
405
+ Write-Host ""
406
+ Write-Host "Dictate is installed."
407
+ if ($NoShortcut -or $NoStartup) {
408
+ Write-Host "Dictate startup shortcut was not installed."
409
+ } else {
410
+ Write-Host "Dictate starts automatically when you sign in."
411
+ }
412
+ Write-Host "Start push-to-talk with a Windows tray icon from the Start Menu shortcut named 'Dictate', or run:"
413
+ Write-Host " .\.venv\Scripts\dictate-tray.cmd"
414
+ Write-Host ""
415
+ Write-Host "Headless push-to-talk daemon:"
416
+ Write-Host " .\.venv\Scripts\dictate-daemon.cmd"
417
+ Write-Host ""
418
+ Write-Host "Control panel:"
419
+ Write-Host " .\.venv\Scripts\dictate-controls.cmd"
420
+ Write-Host ""
421
+ Write-Host "One-shot mode:"
422
+ Write-Host " .\.venv\Scripts\dictate-once.cmd"
package/install.ps1 ADDED
@@ -0,0 +1,87 @@
1
+ param(
2
+ [string]$InstallRoot,
3
+ [string]$ArchiveUrl,
4
+ [switch]$NoVerify,
5
+ [switch]$NoPrepareTurbo,
6
+ [switch]$NoShortcut,
7
+ [switch]$NoStartup,
8
+ [switch]$Wizard,
9
+ [switch]$RecreateVenv
10
+ )
11
+
12
+ $ErrorActionPreference = "Stop"
13
+ $DictateVersion = "2026.6.3"
14
+
15
+ if (-not $ArchiveUrl) {
16
+ $ArchiveUrl = "https://github.com/arcforgelabs/dictate/archive/refs/tags/v$DictateVersion.zip"
17
+ }
18
+
19
+ if (-not $InstallRoot) {
20
+ $base = $env:LOCALAPPDATA
21
+ if (-not $base) {
22
+ $base = Join-Path $HOME "AppData\Local"
23
+ }
24
+ $InstallRoot = Join-Path $base "Dictate"
25
+ }
26
+
27
+ $installRootPath = [System.IO.Path]::GetFullPath($InstallRoot)
28
+ $sourceDir = Join-Path $installRootPath "source"
29
+ $stagingRoot = Join-Path $env:TEMP ("dictate-install-" + [guid]::NewGuid().ToString("N"))
30
+ $archivePath = Join-Path $stagingRoot "dictate.zip"
31
+
32
+ Write-Host "==> Installing Dictate to $installRootPath"
33
+ New-Item -ItemType Directory -Force -Path $stagingRoot | Out-Null
34
+ New-Item -ItemType Directory -Force -Path $installRootPath | Out-Null
35
+
36
+ try {
37
+ if (Test-Path -LiteralPath $ArchiveUrl) {
38
+ Write-Host "==> Copying local archive $ArchiveUrl"
39
+ Copy-Item -Force -LiteralPath $ArchiveUrl -Destination $archivePath
40
+ } else {
41
+ Write-Host "==> Downloading $ArchiveUrl"
42
+ Invoke-WebRequest -UseBasicParsing -Uri $ArchiveUrl -OutFile $archivePath
43
+ }
44
+
45
+ Write-Host "==> Expanding source archive"
46
+ Expand-Archive -Force -Path $archivePath -DestinationPath $stagingRoot
47
+ $expanded = Get-ChildItem -Path $stagingRoot -Directory |
48
+ Where-Object { Test-Path (Join-Path $_.FullName "install-windows.ps1") } |
49
+ Select-Object -First 1
50
+ if (-not $expanded) {
51
+ throw "Downloaded archive did not contain install-windows.ps1."
52
+ }
53
+
54
+ if (Test-Path $sourceDir) {
55
+ Write-Host "==> Replacing existing managed source: $sourceDir"
56
+ Remove-Item -Recurse -Force $sourceDir
57
+ }
58
+ Move-Item -Path $expanded.FullName -Destination $sourceDir
59
+
60
+ $installerScript = if ($Wizard) {
61
+ Join-Path $sourceDir "install-windows-wizard.ps1"
62
+ } else {
63
+ Join-Path $sourceDir "install-windows.ps1"
64
+ }
65
+ $installerArgs = @("-ExecutionPolicy", "Bypass", "-File", $installerScript)
66
+ if (-not $Wizard) {
67
+ if ($NoVerify) { $installerArgs += "-NoVerify" }
68
+ if ($NoPrepareTurbo) { $installerArgs += "-NoPrepareTurbo" }
69
+ if ($NoShortcut) { $installerArgs += "-NoShortcut" }
70
+ if ($NoStartup) { $installerArgs += "-NoStartup" }
71
+ if ($RecreateVenv) { $installerArgs += "-RecreateVenv" }
72
+ }
73
+
74
+ Write-Host "==> Running Dictate Windows installer"
75
+ & powershell @installerArgs
76
+ if ($LASTEXITCODE -ne 0) {
77
+ throw "Dictate Windows installer failed with exit code $LASTEXITCODE."
78
+ }
79
+ } finally {
80
+ if (Test-Path $stagingRoot) {
81
+ Remove-Item -Recurse -Force $stagingRoot
82
+ }
83
+ }
84
+
85
+ Write-Host ""
86
+ Write-Host "Dictate install source: $sourceDir"
87
+ Write-Host "Start Dictate from the Start Menu shortcut named 'Dictate'."