pinggy 0.6.0 → 0.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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDaemonChild
4
- } from "./chunk-4G6SPMMV.js";
4
+ } from "./chunk-TG6LBCPG.js";
5
5
  import {
6
6
  RemoteManagementUnauthorizedError,
7
7
  TunnelOperations,
@@ -13,20 +13,21 @@ import {
13
13
  getRemoteManagementState,
14
14
  initiateRemoteManagement,
15
15
  isDaemonRunning
16
- } from "./chunk-2HGC37SK.js";
17
- import "./chunk-MT44NAXX.js";
16
+ } from "./chunk-GESMZQHI.js";
17
+ import "./chunk-PD7HXVQ3.js";
18
18
  import {
19
19
  DaemonHost
20
- } from "./chunk-I4HXBP5G.js";
21
- import "./chunk-H5XJMFNT.js";
20
+ } from "./chunk-7EQKHVQX.js";
21
+ import "./chunk-Y435RMHQ.js";
22
+ import "./chunk-XZ64ZAGO.js";
22
23
  import {
23
24
  TunnelManager
24
- } from "./chunk-J2U6P376.js";
25
+ } from "./chunk-QNUVQ6KD.js";
25
26
  import "./chunk-UB26QJ4T.js";
26
27
  import {
27
28
  enablePackageLogging
28
- } from "./chunk-76SOSXXR.js";
29
- import "./chunk-GBYF2H4H.js";
29
+ } from "./chunk-MV56U3E6.js";
30
+ import "./chunk-AFMTIELL.js";
30
31
  export {
31
32
  DaemonHost,
32
33
  RemoteManagementUnauthorizedError,
@@ -3,7 +3,7 @@ import {
3
3
  } from "../chunk-UB26QJ4T.js";
4
4
  import {
5
5
  logger
6
- } from "../chunk-76SOSXXR.js";
6
+ } from "../chunk-MV56U3E6.js";
7
7
 
8
8
  // src/workers/file_serve_worker.ts
9
9
  import { parentPort, workerData } from "worker_threads";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinggy",
3
- "version": "0.6.0",
3
+ "version": "0.6.3",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "description": "Create secure, shareable tunnels to your localhost and manage them from the command line. ",
@@ -53,9 +53,12 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "@pinggy/pinggy": "^0.5.3",
56
+ "@xterm/addon-serialize": "^0.14.0",
57
+ "@xterm/headless": "^6.0.0",
56
58
  "blessed": "^0.1.81",
57
59
  "clipboardy": "^5.0.0",
58
60
  "mime": "^4.1.0",
61
+ "node-pty": "^1.1.0",
59
62
  "picocolors": "^1.1.1",
60
63
  "qrcode": "^1.5.4",
61
64
  "winston": "^3.17.0",
@@ -71,7 +74,7 @@
71
74
  "@types/ws": "^8.18.1",
72
75
  "@typescript-eslint/eslint-plugin": "^8.60.0",
73
76
  "@typescript-eslint/parser": "^8.60.0",
74
- "@yao-pkg/pkg": "^6.11.0",
77
+ "@yao-pkg/pkg": "6.19.0",
75
78
  "eslint": "^10.4.0",
76
79
  "jest": "^30.1.2",
77
80
  "ts-jest": "^29.4.1",
@@ -0,0 +1,261 @@
1
+ # Installs or updates the Pinggy CLI on Windows, then signs this machine in as a Pinggy device.
2
+ #
3
+ # irm https://pinggy.io/install.ps1 | iex
4
+ # $env:DEVICE_AGENT_TOKEN='<token>'; irm https://pinggy.io/install.ps1 | iex
5
+ #
6
+ # The binary goes to %LOCALAPPDATA%\Pinggy\bin\pinggy.exe, and that directory goes on the user PATH. No admin.
7
+ #
8
+ # Setting, from the dashboard's command:
9
+ # $env:DEVICE_AGENT_TOKEN a device token. An older pinggy updates without asking, then `pinggy device connect`
10
+ # runs with no email and no code. The token reaches it in its environment, never on a
11
+ # command line. The script removes the variable from this window when it ends
12
+ #
13
+ # Overrides, for dev and tests:
14
+ # $env:PINGGY_VERSION a release tag, such as v0.6.1. Default: the latest release
15
+ # $env:PINGGY_BINARY_URL a full binary URL, file:// included. Replaces the installed binary without a version check
16
+ # $env:PINGGY_MANAGE a non-production dashboard host, passed to `pinggy device login` or `connect` as --manage
17
+ #
18
+ # The source lives in Pinggy-io/cli-js, scripts/install/. A release copies it to pinggy.io.
19
+ # Design: docs/pinggy-devices/install-script-plan.md in the pinggy_backend repo.
20
+ #
21
+ # iex runs this inside the user's own session. So it never calls exit, which would close their window, and every
22
+ # helper and preference is local to Install-Pinggy. Nothing runs until the last line.
23
+
24
+ function Install-Pinggy {
25
+ # Write-Host, because iex also prints the output stream, and these lines are messages, not output.
26
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')]
27
+ param()
28
+
29
+ $ErrorActionPreference = 'Stop'
30
+ # The progress bar makes Invoke-WebRequest many times slower in Windows PowerShell 5.1.
31
+ $ProgressPreference = 'SilentlyContinue'
32
+
33
+ $releasesUrl = 'https://github.com/Pinggy-io/cli-js/releases'
34
+ $installDir = Join-Path $env:LOCALAPPDATA 'Pinggy\bin'
35
+ $installPath = Join-Path $installDir 'pinggy.exe'
36
+ $versionPattern = 'Pinggy CLI version:\s*(\S+)'
37
+ $cpuArchitectureX64 = 9
38
+ $cpuArchitectureArm64 = 12
39
+
40
+ # Win32_Processor describes the machine. PROCESSOR_ARCHITECTURE describes this process, and reads AMD64 in an
41
+ # x64 PowerShell emulated on ARM64.
42
+ function Get-Cpu {
43
+ $architecture = $null
44
+ try {
45
+ $architecture = (Get-CimInstance -ClassName Win32_Processor | Select-Object -First 1).Architecture
46
+ } catch {
47
+ $architecture = $null
48
+ }
49
+ if ($architecture -eq $cpuArchitectureX64) { return 'x64' }
50
+ if ($architecture -eq $cpuArchitectureArm64) { return 'arm64' }
51
+
52
+ $processArchitecture = $env:PROCESSOR_ARCHITEW6432
53
+ if (-not $processArchitecture) { $processArchitecture = $env:PROCESSOR_ARCHITECTURE }
54
+ if ($processArchitecture -eq 'AMD64') { return 'x64' }
55
+ if ($processArchitecture -eq 'ARM64') { return 'arm64' }
56
+ throw "pinggy install: unsupported CPU: $processArchitecture. The pinggy binaries are built for x64 and arm64."
57
+ }
58
+
59
+ # GitHub redirects /releases/latest to /releases/tag/<tag>. Reading the redirect costs no API call, so the
60
+ # API's 60 requests an hour per IP do not apply.
61
+ function Get-TargetTag {
62
+ if ($env:PINGGY_VERSION) {
63
+ if ($env:PINGGY_VERSION.StartsWith('v')) { return $env:PINGGY_VERSION }
64
+ return "v$($env:PINGGY_VERSION)"
65
+ }
66
+ $response = Invoke-WebRequest -Uri "$releasesUrl/latest" -Method Head -UseBasicParsing
67
+ $baseResponse = $response.BaseResponse
68
+ if ($baseResponse.PSObject.Properties['ResponseUri']) {
69
+ # Windows PowerShell 5.1: HttpWebResponse
70
+ $finalUrl = $baseResponse.ResponseUri.AbsoluteUri
71
+ } else {
72
+ # PowerShell 7: HttpResponseMessage
73
+ $finalUrl = $baseResponse.RequestMessage.RequestUri.AbsoluteUri
74
+ }
75
+ $tag = ($finalUrl -split '/')[-1]
76
+ if ($tag -notmatch '^v\d') {
77
+ throw "pinggy install: could not read the latest release tag from $releasesUrl/latest"
78
+ }
79
+ return $tag
80
+ }
81
+
82
+ # The version a binary reports, or '' when it does not run. 'Continue', because Windows PowerShell 5.1 turns a
83
+ # native command's stderr into a terminating error under 'Stop'.
84
+ function Get-BinaryVersion([string] $path) {
85
+ $ErrorActionPreference = 'Continue'
86
+ try {
87
+ $output = & $path --version 2>$null | Out-String
88
+ } catch {
89
+ return ''
90
+ }
91
+ if ($output -match $versionPattern) { return $Matches[1] }
92
+ return ''
93
+ }
94
+
95
+ function Test-Interactive {
96
+ return [Environment]::UserInteractive -and -not [Console]::IsInputRedirected
97
+ }
98
+
99
+ # Enter, or no terminal, means yes. Only an answer starting with n means no.
100
+ function Confirm-Update([string] $question) {
101
+ if (-not (Test-Interactive)) { return $true }
102
+ $answer = Read-Host $question
103
+ return -not ($answer -match '^\s*n')
104
+ }
105
+
106
+ function Save-Binary([string] $url, [string] $destination) {
107
+ if ($url -like 'file://*') {
108
+ # Invoke-WebRequest in PowerShell 7 cannot read file:// URLs.
109
+ Copy-Item -LiteralPath ([Uri] $url).LocalPath -Destination $destination -Force
110
+ } elseif ($url -like 'https://*') {
111
+ Invoke-WebRequest -Uri $url -OutFile $destination -UseBasicParsing
112
+ } else {
113
+ throw 'pinggy install: PINGGY_BINARY_URL must start with https:// or file://'
114
+ }
115
+ }
116
+
117
+ # Windows refuses to overwrite a running .exe, but allows a rename. So the old file moves aside under a unique
118
+ # name, and a later run deletes it once nothing runs it. A .old still locked never blocks the next update.
119
+ function Move-BinaryIntoPlace([string] $newPath) {
120
+ Get-ChildItem -LiteralPath $installDir -Filter 'pinggy.exe.*.old' |
121
+ Remove-Item -Force -ErrorAction SilentlyContinue
122
+ if (Test-Path -LiteralPath $installPath) {
123
+ Move-Item -LiteralPath $installPath -Destination "$installPath.$([DateTime]::UtcNow.Ticks).old"
124
+ }
125
+ Move-Item -LiteralPath $newPath -Destination $installPath
126
+ }
127
+
128
+ function Install-Binary([string] $tag, [string] $cpu) {
129
+ if ($env:PINGGY_BINARY_URL) {
130
+ $url = $env:PINGGY_BINARY_URL
131
+ } else {
132
+ $url = "$releasesUrl/download/$tag/pinggy-win-$cpu.exe"
133
+ }
134
+ New-Item -ItemType Directory -Force -Path $installDir | Out-Null
135
+ $newPath = "$installPath.new.exe"
136
+
137
+ Write-Host "Downloading $url"
138
+ try {
139
+ try {
140
+ Save-Binary $url $newPath
141
+ } catch {
142
+ throw "pinggy install: the download failed: $url. $($_.Exception.Message)"
143
+ }
144
+ $downloadedVersion = Get-BinaryVersion $newPath
145
+ if (-not $downloadedVersion) {
146
+ throw "pinggy install: the downloaded file does not run on this machine (windows-$cpu). " +
147
+ 'The installed pinggy is unchanged.'
148
+ }
149
+ Move-BinaryIntoPlace $newPath
150
+ } finally {
151
+ Remove-Item -LiteralPath $newPath -Force -ErrorAction SilentlyContinue
152
+ }
153
+ Write-Host "Installed pinggy $downloadedVersion to $installPath"
154
+ }
155
+
156
+ # Steps 2 to 6 of the plan: compare with the installed binary, then download only when needed.
157
+ function Install-OrUpdate([string] $cpu) {
158
+ if ($env:PINGGY_BINARY_URL) {
159
+ Install-Binary '' $cpu
160
+ return
161
+ }
162
+
163
+ $tag = Get-TargetTag
164
+ $targetVersion = $tag.Substring(1)
165
+ if (-not (Test-Path -LiteralPath $installPath)) {
166
+ Install-Binary $tag $cpu
167
+ return
168
+ }
169
+
170
+ $installedVersion = Get-BinaryVersion $installPath
171
+ if (-not $installedVersion) {
172
+ Write-Host "$installPath does not run. Replacing it."
173
+ Install-Binary $tag $cpu
174
+ } elseif ($installedVersion -eq $targetVersion) {
175
+ Write-Host "pinggy $installedVersion is up to date."
176
+ } elseif ($env:DEVICE_AGENT_TOKEN) {
177
+ # An older CLI ignores DEVICE_AGENT_TOKEN, so keeping it would leave the machine unconnected.
178
+ Write-Host "Updating pinggy $installedVersion to $targetVersion."
179
+ Install-Binary $tag $cpu
180
+ } elseif (Confirm-Update "pinggy $installedVersion is installed. Update to $($targetVersion)? [Y/n]") {
181
+ Install-Binary $tag $cpu
182
+ } else {
183
+ Write-Host "Keeping pinggy $installedVersion."
184
+ }
185
+ }
186
+
187
+ function Test-PathListHasInstallDir([string] $pathList) {
188
+ $wanted = $installDir.TrimEnd('\')
189
+ foreach ($entry in ($pathList -split ';')) {
190
+ if ([Environment]::ExpandEnvironmentVariables($entry).TrimEnd('\') -ieq $wanted) { return $true }
191
+ }
192
+ return $false
193
+ }
194
+
195
+ # Reads and writes the registry, not [Environment]::GetEnvironmentVariable: that returns %USERPROFILE%-style
196
+ # entries expanded, and writing them back would flatten them.
197
+ function Add-InstallDirToPath {
198
+ $environmentKey = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment', $true)
199
+ try {
200
+ $userPath = [string] $environmentKey.GetValue('Path', '',
201
+ [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
202
+ if (-not (Test-PathListHasInstallDir $userPath)) {
203
+ $entries = @($userPath -split ';' | Where-Object { $_ }) + $installDir
204
+ $environmentKey.SetValue('Path', ($entries -join ';'), [Microsoft.Win32.RegistryValueKind]::ExpandString)
205
+ # Any user variable set through .NET broadcasts WM_SETTINGCHANGE, so new terminals read the new PATH.
206
+ [Environment]::SetEnvironmentVariable('PINGGY_INSTALLER', $null, 'User')
207
+ Write-Host "Added $installDir to your user PATH. New terminals find pinggy."
208
+ }
209
+ } finally {
210
+ $environmentKey.Close()
211
+ }
212
+ if (-not (Test-PathListHasInstallDir $env:Path)) {
213
+ $env:Path = "$installDir;$env:Path"
214
+ }
215
+ }
216
+
217
+ # Runs the installed file by its full path, so an npm-installed pinggy earlier on PATH does not answer.
218
+ function Invoke-DeviceLogin {
219
+ $loginArguments = @('device', 'login')
220
+ if ($env:PINGGY_MANAGE) { $loginArguments += @('--manage', $env:PINGGY_MANAGE) }
221
+
222
+ if (-not (Test-Interactive)) {
223
+ Write-Host "Installed. To sign this machine in, run: pinggy $($loginArguments -join ' ')"
224
+ Write-Host ('On a machine without a keyboard, add the device in the dashboard and run: ' +
225
+ 'pinggy device connect --token <TOKEN>')
226
+ return
227
+ }
228
+ Write-Host ''
229
+ & $installPath @loginArguments
230
+ }
231
+
232
+ # Runs the installed file by its full path, as Invoke-DeviceLogin does. connect reads DEVICE_AGENT_TOKEN from
233
+ # the environment it inherits, and reads no key, so it runs the same with no console.
234
+ function Invoke-DeviceConnect {
235
+ $connectArguments = @('device', 'connect')
236
+ if ($env:PINGGY_MANAGE) { $connectArguments += @('--manage', $env:PINGGY_MANAGE) }
237
+ Write-Host ''
238
+ & $installPath @connectArguments
239
+ }
240
+
241
+ # Older Windows PowerShell 5.1 setups offer less than TLS 1.2, and GitHub refuses them.
242
+ [Net.ServicePointManager]::SecurityProtocol =
243
+ [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
244
+
245
+ try {
246
+ $cpu = Get-Cpu
247
+ Install-OrUpdate $cpu
248
+ Add-InstallDirToPath
249
+ if ($env:DEVICE_AGENT_TOKEN) {
250
+ Invoke-DeviceConnect
251
+ } else {
252
+ Invoke-DeviceLogin
253
+ }
254
+ } finally {
255
+ # iex runs in the user's own session, so every later command in this window would carry the token. A
256
+ # finally runs after an error and after Ctrl+C too.
257
+ Remove-Item -Path Env:\DEVICE_AGENT_TOKEN -ErrorAction SilentlyContinue
258
+ }
259
+ }
260
+
261
+ Install-Pinggy