@xevy/heny-connect 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -15
- package/bin/heny-connect.mjs +106 -39
- package/lib/browser-controller.mjs +242 -0
- package/lib/cdp.mjs +95 -0
- package/lib/network-policy.mjs +102 -0
- package/lib/state.mjs +54 -0
- package/lib/validating-proxy.mjs +70 -0
- package/lib/worker.mjs +241 -0
- package/package.json +5 -3
- package/windows/heny-connect-tray.ps1 +264 -0
- package/windows/heny-error.ico +0 -0
- package/windows/heny-paused.ico +0 -0
- package/windows/heny.ico +0 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[Parameter(Mandatory = $true)][string] $NodePath,
|
|
3
|
+
[Parameter(Mandatory = $true)][string] $CliPath,
|
|
4
|
+
[Parameter(Mandatory = $true)][string] $HomePath,
|
|
5
|
+
[string] $IconPath,
|
|
6
|
+
[switch] $Install,
|
|
7
|
+
[switch] $Uninstall
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
$ErrorActionPreference = "Stop"
|
|
11
|
+
$taskName = "HenyConnect"
|
|
12
|
+
$powershellPath = Join-Path $env:SystemRoot "System32\WindowsPowerShell\v1.0\powershell.exe"
|
|
13
|
+
$statusPath = Join-Path $HomePath "worker-status.json"
|
|
14
|
+
$trayStatusPath = Join-Path $HomePath "tray-status.json"
|
|
15
|
+
$pausePath = Join-Path $HomePath "paused"
|
|
16
|
+
$stopPath = Join-Path $HomePath "stopping"
|
|
17
|
+
if (-not $IconPath) { $IconPath = Join-Path $PSScriptRoot "heny.ico" }
|
|
18
|
+
|
|
19
|
+
function Get-TaskArguments {
|
|
20
|
+
return "-NoLogo -NoProfile -STA -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$PSCommandPath`" -NodePath `"$NodePath`" -CliPath `"$CliPath`" -HomePath `"$HomePath`" -IconPath `"$IconPath`""
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function Install-HenyTask {
|
|
24
|
+
param([bool] $StartNow)
|
|
25
|
+
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
|
26
|
+
if ($existing) {
|
|
27
|
+
Stop-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
|
28
|
+
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
|
|
29
|
+
}
|
|
30
|
+
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent().Name
|
|
31
|
+
$action = New-ScheduledTaskAction -Execute $powershellPath -Argument (Get-TaskArguments)
|
|
32
|
+
$trigger = New-ScheduledTaskTrigger -AtLogOn -User $currentUser
|
|
33
|
+
$principal = New-ScheduledTaskPrincipal -UserId $currentUser -LogonType Interactive -RunLevel Limited
|
|
34
|
+
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit ([TimeSpan]::Zero) -MultipleInstances IgnoreNew -StartWhenAvailable
|
|
35
|
+
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings | Out-Null
|
|
36
|
+
if ($StartNow) { Start-ScheduledTask -TaskName $taskName }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ($Uninstall) {
|
|
40
|
+
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
|
41
|
+
if ($existing) {
|
|
42
|
+
Stop-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
|
43
|
+
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
|
|
44
|
+
}
|
|
45
|
+
Write-Output "Heny Connect start-at-sign-in removed. Quit the tray to end the current session."
|
|
46
|
+
exit 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if ($Install) {
|
|
50
|
+
Install-HenyTask -StartNow $true
|
|
51
|
+
Write-Output "Heny Connect installed. It is running in the system tray and will start at sign-in."
|
|
52
|
+
exit 0
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
$createdNew = $false
|
|
56
|
+
$mutex = [Threading.Mutex]::new($true, "Local\HenyConnectTray", [ref] $createdNew)
|
|
57
|
+
if (-not $createdNew) { $mutex.Dispose(); exit 0 }
|
|
58
|
+
|
|
59
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
60
|
+
Add-Type -AssemblyName System.Drawing
|
|
61
|
+
[Windows.Forms.Application]::EnableVisualStyles()
|
|
62
|
+
|
|
63
|
+
$iconAvailable = [Drawing.Icon]::new($IconPath)
|
|
64
|
+
$iconPaused = [Drawing.Icon]::new((Join-Path $PSScriptRoot "heny-paused.ico"))
|
|
65
|
+
$iconError = [Drawing.Icon]::new((Join-Path $PSScriptRoot "heny-error.ico"))
|
|
66
|
+
$notifyIcon = [Windows.Forms.NotifyIcon]::new()
|
|
67
|
+
$notifyIcon.Icon = $iconPaused
|
|
68
|
+
$notifyIcon.Text = "Heny Connect - Browser starting"
|
|
69
|
+
$notifyIcon.Visible = $true
|
|
70
|
+
|
|
71
|
+
$menu = [Windows.Forms.ContextMenuStrip]::new()
|
|
72
|
+
$titleItem = [Windows.Forms.ToolStripMenuItem]::new("Heny Connect")
|
|
73
|
+
$titleItem.Enabled = $false
|
|
74
|
+
$titleItem.Font = [Drawing.Font]::new($titleItem.Font, [Drawing.FontStyle]::Bold)
|
|
75
|
+
$statusItem = [Windows.Forms.ToolStripMenuItem]::new("Status: Browser starting")
|
|
76
|
+
$statusItem.Enabled = $false
|
|
77
|
+
$actionItem = [Windows.Forms.ToolStripMenuItem]::new("Current action: None")
|
|
78
|
+
$actionItem.Enabled = $false
|
|
79
|
+
$openItem = [Windows.Forms.ToolStripMenuItem]::new("Open Heny")
|
|
80
|
+
$pauseItem = [Windows.Forms.ToolStripMenuItem]::new("Pause")
|
|
81
|
+
$reconnectItem = [Windows.Forms.ToolStripMenuItem]::new("Reconnect now")
|
|
82
|
+
$startItem = [Windows.Forms.ToolStripMenuItem]::new("Start at sign-in")
|
|
83
|
+
$startItem.Checked = [bool] (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue)
|
|
84
|
+
$quitItem = [Windows.Forms.ToolStripMenuItem]::new("Quit")
|
|
85
|
+
foreach ($item in @($titleItem, $statusItem, $actionItem, [Windows.Forms.ToolStripSeparator]::new(), $openItem, $pauseItem, $reconnectItem, $startItem, [Windows.Forms.ToolStripSeparator]::new(), $quitItem)) { $null = $menu.Items.Add($item) }
|
|
86
|
+
$notifyIcon.ContextMenuStrip = $menu
|
|
87
|
+
|
|
88
|
+
$script:workerProcess = $null
|
|
89
|
+
$script:nextStart = Get-Date
|
|
90
|
+
$script:paused = Test-Path -LiteralPath $pausePath
|
|
91
|
+
$script:exiting = $false
|
|
92
|
+
|
|
93
|
+
function Set-HenyStatus {
|
|
94
|
+
param([string] $State, [string] $Action)
|
|
95
|
+
$label = switch ($State) {
|
|
96
|
+
"available" { "Available" }
|
|
97
|
+
"working" { "Working" }
|
|
98
|
+
"paused" { "Paused" }
|
|
99
|
+
"pausing" { "Pausing" }
|
|
100
|
+
"browser_starting" { "Browser starting" }
|
|
101
|
+
"error" { "Error" }
|
|
102
|
+
"offline" { "Offline" }
|
|
103
|
+
default { "Connecting" }
|
|
104
|
+
}
|
|
105
|
+
$statusItem.Text = "Status: $label"
|
|
106
|
+
$actionItem.Text = if ($Action) { "Current action: $Action" } else { "Current action: None" }
|
|
107
|
+
$notifyIcon.Text = "Heny Connect - $label"
|
|
108
|
+
$notifyIcon.Icon = if ($State -in @("available", "working")) { $iconAvailable } elseif ($State -eq "error") { $iconError } else { $iconPaused }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function Write-TrayStatus {
|
|
112
|
+
param([string] $State, [int] $ExitCode = 0, [string] $Detail = "")
|
|
113
|
+
@{ state = $State; exitCode = $ExitCode; detail = $Detail.Substring(0, [Math]::Min(200, $Detail.Length)); updatedAt = [DateTime]::UtcNow.ToString("o") } | ConvertTo-Json -Compress | Set-Content -LiteralPath $trayStatusPath -Encoding UTF8
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function Get-HenyUrl {
|
|
117
|
+
try {
|
|
118
|
+
$state = Get-Content -LiteralPath (Join-Path $HomePath ".heny-connect.json") -Raw | ConvertFrom-Json
|
|
119
|
+
return ([string] $state.server).TrimEnd("/") + "/desktop"
|
|
120
|
+
} catch { return "https://heny.vyte.dev/desktop" }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function Stop-Worker {
|
|
124
|
+
if (-not $script:workerProcess) { return }
|
|
125
|
+
try {
|
|
126
|
+
if (-not $script:workerProcess.HasExited) {
|
|
127
|
+
New-Item -ItemType Directory -Path $HomePath -Force | Out-Null
|
|
128
|
+
Set-Content -LiteralPath $pausePath -Value "paused" -NoNewline
|
|
129
|
+
Set-Content -LiteralPath $stopPath -Value "stop" -NoNewline
|
|
130
|
+
$limit = (Get-Date).AddSeconds(5)
|
|
131
|
+
while (-not $script:workerProcess.HasExited -and (Get-Date) -lt $limit) {
|
|
132
|
+
try {
|
|
133
|
+
$state = Get-Content -LiteralPath $statusPath -Raw | ConvertFrom-Json
|
|
134
|
+
if ($state.state -eq "paused") { break }
|
|
135
|
+
} catch {}
|
|
136
|
+
Start-Sleep -Milliseconds 200
|
|
137
|
+
}
|
|
138
|
+
if (-not $script:workerProcess.HasExited) {
|
|
139
|
+
& taskkill.exe /PID ([string] $script:workerProcess.Id) /T /F 2>$null | Out-Null
|
|
140
|
+
$script:workerProcess.WaitForExit(2000) | Out-Null
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
$script:workerProcess.Dispose()
|
|
144
|
+
} catch {}
|
|
145
|
+
$script:workerProcess = $null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function Start-Worker {
|
|
149
|
+
if ($script:workerProcess -or $script:paused -or $script:exiting) { return }
|
|
150
|
+
Remove-Item -LiteralPath $stopPath -Force -ErrorAction SilentlyContinue
|
|
151
|
+
$startInfo = [Diagnostics.ProcessStartInfo]::new()
|
|
152
|
+
$startInfo.FileName = $NodePath
|
|
153
|
+
$startInfo.Arguments = "`"$CliPath`" run"
|
|
154
|
+
$startInfo.UseShellExecute = $false
|
|
155
|
+
$startInfo.CreateNoWindow = $true
|
|
156
|
+
$startInfo.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden
|
|
157
|
+
$startInfo.EnvironmentVariables["HENY_CONNECT_HOME"] = $HomePath
|
|
158
|
+
$process = [Diagnostics.Process]::new()
|
|
159
|
+
$process.StartInfo = $startInfo
|
|
160
|
+
try {
|
|
161
|
+
if (-not $process.Start()) { throw "The worker did not start." }
|
|
162
|
+
$script:workerProcess = $process
|
|
163
|
+
Write-TrayStatus -State "worker_started"
|
|
164
|
+
Set-HenyStatus -State "browser_starting"
|
|
165
|
+
} catch {
|
|
166
|
+
$process.Dispose()
|
|
167
|
+
Write-TrayStatus -State "worker_start_failed" -ExitCode -1 -Detail $_.Exception.Message
|
|
168
|
+
Set-HenyStatus -State "error"
|
|
169
|
+
$script:nextStart = (Get-Date).AddSeconds(15)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function Request-Pause {
|
|
174
|
+
New-Item -ItemType Directory -Path $HomePath -Force | Out-Null
|
|
175
|
+
Set-Content -LiteralPath $pausePath -Value "paused" -NoNewline
|
|
176
|
+
$script:paused = $true
|
|
177
|
+
$pauseItem.Text = "Resume"
|
|
178
|
+
Set-HenyStatus -State "pausing"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function Request-Resume {
|
|
182
|
+
Remove-Item -LiteralPath $pausePath -Force -ErrorAction SilentlyContinue
|
|
183
|
+
Remove-Item -LiteralPath $stopPath -Force -ErrorAction SilentlyContinue
|
|
184
|
+
$script:paused = $false
|
|
185
|
+
$pauseItem.Text = "Pause"
|
|
186
|
+
$script:nextStart = Get-Date
|
|
187
|
+
Set-HenyStatus -State "browser_starting"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
$openHeny = { Start-Process (Get-HenyUrl) }
|
|
191
|
+
$openItem.Add_Click($openHeny)
|
|
192
|
+
$notifyIcon.Add_DoubleClick($openHeny)
|
|
193
|
+
$pauseItem.Add_Click({ if ($script:paused) { Request-Resume } else { Request-Pause } })
|
|
194
|
+
$reconnectItem.Add_Click({ Request-Pause; Stop-Worker; Request-Resume })
|
|
195
|
+
|
|
196
|
+
$startItem.Add_Click({
|
|
197
|
+
try {
|
|
198
|
+
if ($startItem.Checked) {
|
|
199
|
+
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
|
200
|
+
$startItem.Checked = $false
|
|
201
|
+
} else {
|
|
202
|
+
Install-HenyTask -StartNow $false
|
|
203
|
+
$startItem.Checked = $true
|
|
204
|
+
}
|
|
205
|
+
} catch { Set-HenyStatus -State "error" }
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
$quitItem.Add_Click({
|
|
209
|
+
$script:exiting = $true
|
|
210
|
+
$timer.Stop()
|
|
211
|
+
Request-Pause
|
|
212
|
+
$limit = (Get-Date).AddSeconds(5)
|
|
213
|
+
while ((Get-Date) -lt $limit) {
|
|
214
|
+
try {
|
|
215
|
+
$state = Get-Content -LiteralPath $statusPath -Raw | ConvertFrom-Json
|
|
216
|
+
if ($state.state -eq "paused") { break }
|
|
217
|
+
} catch {}
|
|
218
|
+
Start-Sleep -Milliseconds 200
|
|
219
|
+
}
|
|
220
|
+
Stop-Worker
|
|
221
|
+
Remove-Item -LiteralPath $pausePath -Force -ErrorAction SilentlyContinue
|
|
222
|
+
Remove-Item -LiteralPath $stopPath -Force -ErrorAction SilentlyContinue
|
|
223
|
+
$notifyIcon.Visible = $false
|
|
224
|
+
[Windows.Forms.Application]::ExitThread()
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
$timer = [Windows.Forms.Timer]::new()
|
|
228
|
+
$timer.Interval = 1000
|
|
229
|
+
$timer.Add_Tick({
|
|
230
|
+
if ($script:workerProcess -and $script:workerProcess.HasExited) {
|
|
231
|
+
$code = $script:workerProcess.ExitCode
|
|
232
|
+
Write-TrayStatus -State "worker_exited" -ExitCode $code
|
|
233
|
+
$script:workerProcess.Dispose()
|
|
234
|
+
$script:workerProcess = $null
|
|
235
|
+
if (-not $script:paused) {
|
|
236
|
+
Set-HenyStatus -State "error"
|
|
237
|
+
$script:nextStart = if ($code -in @(2, 3)) { [DateTime]::MaxValue } else { (Get-Date).AddSeconds(15) }
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if ($script:workerProcess) {
|
|
241
|
+
try {
|
|
242
|
+
$state = Get-Content -LiteralPath $statusPath -Raw | ConvertFrom-Json
|
|
243
|
+
if ($script:paused -and $state.state -ne "paused") { Set-HenyStatus -State "pausing" } else { Set-HenyStatus -State ([string] $state.state) -Action ([string] $state.currentAction) }
|
|
244
|
+
} catch {}
|
|
245
|
+
} elseif ($script:paused) { Set-HenyStatus -State "paused" }
|
|
246
|
+
if (-not $script:paused -and -not $script:workerProcess -and (Get-Date) -ge $script:nextStart) { Start-Worker }
|
|
247
|
+
})
|
|
248
|
+
$timer.Start()
|
|
249
|
+
if ($script:paused) { Set-HenyStatus -State "paused"; $pauseItem.Text = "Resume" } else { Start-Worker }
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
[Windows.Forms.Application]::Run()
|
|
253
|
+
} finally {
|
|
254
|
+
$timer.Stop()
|
|
255
|
+
Stop-Worker
|
|
256
|
+
$notifyIcon.Visible = $false
|
|
257
|
+
$notifyIcon.Dispose()
|
|
258
|
+
$menu.Dispose()
|
|
259
|
+
$iconAvailable.Dispose()
|
|
260
|
+
$iconPaused.Dispose()
|
|
261
|
+
$iconError.Dispose()
|
|
262
|
+
if ($createdNew) { $mutex.ReleaseMutex() }
|
|
263
|
+
$mutex.Dispose()
|
|
264
|
+
}
|
|
Binary file
|
|
Binary file
|
package/windows/heny.ico
ADDED
|
Binary file
|