-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.ps1
More file actions
433 lines (381 loc) · 19.5 KB
/
Copy pathinstall.ps1
File metadata and controls
433 lines (381 loc) · 19.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
param(
[string]$RepoUrl = "https://github.com/AliSharjeell/Termote.git",
[string]$Branch = "master"
)
$ErrorActionPreference = "Continue"
Write-Host ""
Write-Host " ████████╗███████╗██████╗ ███╗ ███╗██████╗ ████████╗███████╗" -ForegroundColor Cyan
Write-Host " ╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔═══██╗╚══██╔══╝██╔════╝" -ForegroundColor Cyan
Write-Host " ██║ █████╗ ██████╔╝██╔████╔██║██║ ██║ ██║ █████╗ " -ForegroundColor Cyan
Write-Host " ██║ ██╔══╝ ██╔══██╗██║╚██╔╝██║██║ ██║ ██║ ██╔══╝ " -ForegroundColor Cyan
Write-Host " ██║ ███████╗██║ ██║██║ ╚═╝ ██║╚██████╔╝ ██║ ███████╗" -ForegroundColor Cyan
Write-Host " ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝" -ForegroundColor Cyan
Write-Host ""
Write-Host " Turn any browser into a full-powered, multi-pane terminal for your PC — instantly. No SSH, no tmux, no setup." -ForegroundColor Gray
Write-Host ""
$installDir = "$env:USERPROFILE\termote"
$backendDir = "$installDir\backend"
$shimDir = "$env:USERPROFILE\.termote-bin"
# 1. Clone or update the repo
if (-not (Test-Path $installDir)) {
Write-Host "[1/8] Cloning Termote repository..." -ForegroundColor Yellow
git clone --depth 1 $RepoUrl $installDir
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Failed to clone repository. Is Git installed?" -ForegroundColor Red
exit 1
}
} else {
Write-Host "[1/8] Updating existing Termote installation..." -ForegroundColor Yellow
Set-Location $installDir
git pull origin $Branch
# Sync updated scripts and binary from repo to installed location
# $PSScriptRoot is the repo on disk (has our edits), $backendDir is the target install
Write-Host " Syncing updated files to installed location..." -ForegroundColor Gray
Copy-Item -Path "$PSScriptRoot\start.ps1" -Destination "$backendDir\start.ps1" -Force
Copy-Item -Path "$PSScriptRoot\install.ps1" -Destination "$backendDir\install.ps1" -Force
# Delete old root-level stale files
Remove-Item "$installDir\start.ps1" -Force -ErrorAction SilentlyContinue
Remove-Item "$installDir\install.ps1" -Force -ErrorAction SilentlyContinue
}
# 2. Install Rust if not present
if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
Write-Host "[2/8] Installing Rust (first-time only, ~2 min)..." -ForegroundColor Yellow
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri "https://win.rustup.rs" -OutFile "$env:TEMP\rustup-init.exe"
& "$env:TEMP\rustup-init.exe" -y -q --default-toolchain stable
Remove-Item "$env:TEMP\rustup-init.exe" -Force -ErrorAction SilentlyContinue
$env:Path += ";$env:USERPROFILE\.cargo\bin"
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","User") + ";" + [System.Environment]::GetEnvironmentVariable("Path","Machine")
Write-Host " Rust installed successfully!" -ForegroundColor Green
} else {
Write-Host "[2/8] Rust already installed, skipping..." -ForegroundColor Gray
}
# 3. Installing Dev Tunnels with a sanity check
Write-Host "[3/8] Installing Microsoft Dev Tunnels CLI..." -ForegroundColor Yellow
$devtunnelPath = "$installDir\bin\devtunnel.exe"
# Ensure bin directory exists
if (-not (Test-Path "$installDir\bin")) {
New-Item -Type Directory -Force "$installDir\bin" | Out-Null
}
# Use curl.exe (built into Windows 10/11) — handles GitHub redirects reliably
Write-Host " Downloading devtunnel.exe..." -ForegroundColor Gray
curl.exe -L --silent --show-error -o $devtunnelPath "https://github.com/microsoft/dev-tunnels/releases/latest/download/devtunnel-win-x64.exe"
# Sanity Check: If the file is smaller than 1MB, it's definitely a failed download
if (-not (Test-Path $devtunnelPath) -or (Get-Item $devtunnelPath).Length -lt 1MB) {
Write-Host " curl.exe failed, trying direct MS download link..." -ForegroundColor Yellow
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri "https://aka.ms/TunnelsCliDownload/win-x64" -OutFile $devtunnelPath -UseBasicParsing
}
if (-not (Test-Path $devtunnelPath) -or (Get-Item $devtunnelPath).Length -lt 1MB) {
Write-Host "ERROR: All download attempts failed." -ForegroundColor Red
Write-Host " Manual fix: Download devtunnel.exe from https://aka.ms/TunnelsCliDownload/win-x64" -ForegroundColor Yellow
Write-Host " Save it to: $devtunnelPath" -ForegroundColor Yellow
exit 1
}
Write-Host " Dev Tunnels CLI installed and verified!" -ForegroundColor Green
# 4. Login to Microsoft Dev Tunnels (Official CLI Method)
Write-Host "[4/8] Microsoft Dev Tunnels login..." -ForegroundColor Yellow
Write-Host " A browser window will now open for authentication." -ForegroundColor Cyan
Write-Host " If the browser doesn't open, copy the link printed below." -ForegroundColor Gray
Write-Host ""
# Call the CLI directly to handle the login flow
& $devtunnelPath user login -g
if ($LASTEXITCODE -ne 0) {
Write-Host "WARNING: Login was not completed or failed." -ForegroundColor Yellow
} else {
Write-Host " Login successful!" -ForegroundColor Green
}
# 5. Compile the Rust backend
Write-Host "[5/8] Compiling Rust backend..." -ForegroundColor Yellow
Set-Location $backendDir
cargo build --release
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Rust compilation failed." -ForegroundColor Red
exit 1
}
Write-Host " Backend compiled successfully!" -ForegroundColor Green
# Kill running termote so we can overwrite the exe
Stop-Process -Name "termote" -Force -ErrorAction SilentlyContinue
Start-Sleep -Milliseconds 500
# Copy freshly compiled binary to installed location
$targetDir = "$backendDir\target\release"
if (-not (Test-Path $targetDir)) {
New-Item -Type Directory -Force $targetDir | Out-Null
}
Copy-Item -Path "$PSScriptRoot\target\release\termote.exe" `
-Destination "$targetDir\termote.exe" -Force
Write-Host " Binary synced to installed location." -ForegroundColor Green
# 6. Create shim directory and files
Write-Host "[6/8] Setting up termote commands..." -ForegroundColor Yellow
if (-not (Test-Path $shimDir)) {
New-Item -Type Directory -Force $shimDir | Out-Null
}
$termoteShimContent = @'
# Smart termote launcher - VS Code style single instance
param(
[string]$InitialDir = ""
)
$backendDir = "$env:USERPROFILE\termote\backend"
$termoteDir = "$env:USERPROFILE\termote\backend"
function Send-IpcCommand($cmd) {
try {
$client = New-Object System.Net.Sockets.TcpClient
$client.Connect("127.0.0.1", 9091)
$stream = $client.GetStream()
$writer = New-Object System.IO.StreamWriter($stream)
$writer.WriteLine($cmd)
$writer.Flush()
$stream.Close()
$client.Close()
return $true
} catch { return $false }
}
# If directory was passed as argument, use it; otherwise use current location
if ($InitialDir -and (Test-Path $InitialDir)) {
$cwd = $InitialDir
} else {
$cwd = (Get-Location).Path
}
# 1. Check if the process exists AT ALL (prevents race conditions during boot)
$termoteProc = Get-Process -Name "termote" -ErrorAction SilentlyContinue
if ($termoteProc) {
Write-Host "Termote is already running (or booting up). Waiting for it to be ready..." -ForegroundColor DarkGray
# Wait up to 15 seconds for the boot to finish and port 9090 to open
$isReady = $false
for ($i = 0; $i -lt 15; $i++) {
try {
$resp = Invoke-WebRequest -Uri "http://127.0.0.1:9090/health" -UseBasicParsing -TimeoutSec 1 -EA SilentlyContinue
if ($resp.StatusCode -eq 200) { $isReady = $true; break }
} catch { }
Start-Sleep -Seconds 1
}
if ($isReady) {
Write-Host "Termote is ready. Sending open_dir IPC..." -ForegroundColor Cyan
$sent = Send-IpcCommand "open_dir:$cwd"
if ($sent) {
Write-Host "New pane opened at: $cwd" -ForegroundColor Green
exit 0
} else {
Write-Host "Failed to talk to existing Termote instance. It might be frozen." -ForegroundColor Red
}
} else {
Write-Host "Existing Termote instance is hung and won't respond. Restarting it..." -ForegroundColor Yellow
Stop-Process -Name "termote" -Force -EA SilentlyContinue
Stop-Process -Name "devtunnel" -Force -EA SilentlyContinue
Start-Sleep -Seconds 1
}
}
# 2. If we get here, no instances are running. Start fresh (cold start)!
Write-Host "Starting fresh Termote server (cold start)..." -ForegroundColor Green
# Set initial directory for cold start - backend will auto-spawn first terminal here
$env:TERMOTE_INITIAL_DIR = $cwd
# Start the backend (this launches dev tunnel and backend)
& "$termoteDir\start.ps1"
# Wait for the backend to be ready, then auto-open browser
Write-Host "Waiting for Termote backend to be ready..." -ForegroundColor DarkGray
$isReady = $false
for ($i = 0; $i -lt 20; $i++) {
try {
$resp = Invoke-WebRequest -Uri "http://127.0.0.1:9090/health" -UseBasicParsing -TimeoutSec 1 -EA SilentlyContinue
if ($resp.StatusCode -eq 200) { $isReady = $true; break }
} catch { }
Start-Sleep -Seconds 1
}
if ($isReady) {
# Read .env to get auth token for launch URL
$envFile = "$backendDir\.env"
$token = "unknown"
if (Test-Path $envFile) {
$content = Get-Content $envFile -Raw
if ($content -match 'AUTH_TOKEN=([^\s]+)') { $token = $matches[1].Trim() }
if ($content -match 'TUNNEL_URL=([^\s]+)') { $tunnelUrl = $matches[1].Trim() }
}
# Get frontend URL from environment or use default
$frontendUrl = if ($env:FRONTEND_URL) { $env:FRONTEND_URL } else { "https://termote.vercel.app" }
# If we have tunnel URL, construct full launch URL
if ($tunnelUrl) {
$launchUrl = "$frontendUrl/?tunnel=$([Uri]::EscapeDataString($tunnelUrl + '/ws'))&token=$token"
} else {
# Fallback to localhost
$launchUrl = "$frontendUrl/?tunnel=$([Uri]::EscapeDataString('ws://127.0.0.1:9090'))&token=$token"
}
Write-Host ""
Write-Host "Opening Termote in browser..." -ForegroundColor Cyan
Start-Process $launchUrl
} else {
Write-Host "Backend failed to start within 20 seconds." -ForegroundColor Red
}
'@
Set-Content -Path "$shimDir\termote.ps1" -Value $termoteShimContent -Encoding UTF8
$cmdLines = @(
"@echo off"
"powershell -NoProfile -ExecutionPolicy Bypass -Command `"`& '%~dp0termote.ps1' %*`""
)
Set-Content -Path "$shimDir\termote.cmd" -Value $cmdLines -Encoding ASCII
$killLines = @(
'# Termote Kill Script'
'Write-Host "Stopping Termote..." -ForegroundColor Yellow'
'Get-Process -Name "termote" -EA SilentlyContinue | Stop-Process -Force -EA SilentlyContinue'
'Get-Process -Name "devtunnel" -EA SilentlyContinue | Stop-Process -Force -EA SilentlyContinue'
'Get-NetTCPConnection -LocalPort 9090 -EA SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -EA SilentlyContinue }'
'Get-NetTCPConnection -LocalPort 9091 -EA SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -EA SilentlyContinue }'
'Write-Host "All Termote instances stopped." -ForegroundColor Green'
)
Set-Content -Path "$shimDir\termote-kill.ps1" -Value $killLines -Encoding UTF8
$killCmdLines = @(
"@echo off"
"powershell -NoProfile -ExecutionPolicy Bypass -Command `"`& '%~dp0termote-kill.ps1'`""
)
Set-Content -Path "$shimDir\termote-kill.cmd" -Value $killCmdLines -Encoding ASCII
# termote-link: Display tunnel URL, password, and Ctrl+clickable share link
$linkLines = @(
'# Termote Link Display'
'$backendDir = "$env:USERPROFILE\termote\backend"'
'$envFile = "$backendDir\.env"'
''
'if (-not (Test-Path $envFile)) {'
' Write-Host "No .env file found. Is Termote running?" -ForegroundColor Red'
' exit 1'
'}'
''
'$content = Get-Content $envFile -Raw'
'$tunnelUrl = if ($content -match ''TUNNEL_URL=(.+)'') { $Matches[1].Trim() } else { $null }'
'$token = if ($content -match ''AUTH_TOKEN=(.+)'') { $Matches[1].Trim() } else { $null }'
''
'if (-not $tunnelUrl -or -not $token) {'
' Write-Host "Tunnel URL or token not found in .env." -ForegroundColor Red'
' exit 1'
'}'
''
'$shareLink = "https://termote.vercel.app/?tunnel=$([Uri]::EscapeDataString($tunnelUrl))&token=$([Uri]::EscapeDataString($token))"'
'$wsLink = $tunnelUrl'
''
'# OSC 8 hyperlink: ESC ] 8 ; ; URL ST'
'# Format: `e]8;;URL`e\TEXT`e]8;;`e\'
'$esc = "`e"'
'$hyperlink = "$esc]8;;$shareLink$esc\$esc]8;;$esc\"'
''
$launchUrl = "https://termote.vercel.app/?tunnel=$([Uri]::EscapeDataString($wsUrl + '/ws'))&token=$token"
$esc = [char]27
$clickableLink = "${esc}]8;;${launchUrl}${esc}\Open Termote in Browser${esc}]8;;${esc}\"
Write-Host ""
Write-Host " Termote Connection Info" -ForegroundColor Cyan
Write-Host " ─────────────────────" -ForegroundColor Cyan
Write-Host " Tunnel (WSS): " -NoNewline; Write-Host $wsUrl -ForegroundColor White
Write-Host " Password: " -NoNewline; Write-Host $token -ForegroundColor White
Write-Host " Share Link: " -NoNewline; Write-Host $clickableLink -ForegroundColor Green
Write-Host ""
)
Set-Content -Path "$shimDir\termote-link.ps1" -Value $linkLines -Encoding UTF8
$linkCmdLines = @(
"@echo off"
"powershell -NoProfile -ExecutionPolicy Bypass -Command `"`& '%~dp0termote-link.ps1'`""
)
Set-Content -Path "$shimDir\termote-link.cmd" -Value $linkCmdLines -Encoding ASCII
# termote-log: Tail the real-time backend log file
$logLines = @(
'# Termote Log Viewer'
''
'# Find the latest rolling log file (tracing-appender uses prefix.YYYY-MM-DD pattern)'
'$logDir = $env:TEMP'
'$logFiles = Get-ChildItem -Path $logDir -Filter "termote.log.*" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending'
'$logPath = if ($logFiles) { $logFiles[0].FullName } else { "$logDir\termote.log" }'
''
'if (-not (Test-Path $logPath)) {'
' Write-Host "No log file found in: $logDir" -ForegroundColor Red'
' Write-Host "Is Termote running?" -ForegroundColor Yellow'
' exit 1'
'}'
''
'Write-Host "Tailing: $logPath" -ForegroundColor Cyan'
'Write-Host "Press Ctrl+C to stop." -ForegroundColor Gray'
'Write-Host ""'
'Get-Content -Path $logPath -Wait -Tail 50'
)
Set-Content -Path "$shimDir\termote-log.ps1" -Value $logLines -Encoding UTF8
$logCmdLines = @(
"@echo off"
"powershell -NoProfile -ExecutionPolicy Bypass -Command `"& '%~dp0termote-log.ps1' %*`""
)
Set-Content -Path "$shimDir\termote-log.cmd" -Value $logCmdLines -Encoding ASCII
# Add shimDir to PATH if not already there
$updateLines = @(
'# Termote Update Script'
'$RepoUrl = "https://raw.githubusercontent.com/AliSharjeell/Termote/master/install.ps1"'
'Write-Host "Fetching latest Termote installation script..." -ForegroundColor White'
'try {'
' $script = Invoke-RestMethod -Uri $RepoUrl -TimeoutSec 30 -ErrorAction Stop'
' if ($script) {'
' Write-Host "Script downloaded. Executing installation..." -ForegroundColor Green'
' Invoke-Expression $script'
' }'
'} catch {'
' Write-Host "Failed to fetch update script: $_" -ForegroundColor Red'
' exit 1'
'}'
)
Set-Content -Path "$shimDir\termote-update.ps1" -Value $updateLines -Encoding UTF8
$updateCmdLines = @(
"@echo off"
"powershell -NoProfile -ExecutionPolicy Bypass -Command `"& '%~dp0termote-update.ps1' %*`""
)
Set-Content -Path "$shimDir\termote-update.cmd" -Value $updateCmdLines -Encoding ASCII
# Add shimDir to PATH if not already there
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
if ($userPath -notlike "*$shimDir*") {
[Environment]::SetEnvironmentVariable("PATH", "$userPath;$shimDir", "User")
$env:PATH += ";$shimDir"
Write-Host " Added to PATH." -ForegroundColor Green
}
Write-Host " Commands installed." -ForegroundColor Green
# 7. Add "Open with Termote" context menu
Write-Host "[7/8] Adding Windows Explorer context menu..." -ForegroundColor Yellow
$termoteFileHandler = "$shimDir\termote-file.ps1"
$handlerLines = @(
'# Context menu handler for "Open with Termote"'
'$rawPath = $args[0]'
'if (-not $rawPath) { $rawPath = (Get-Location).Path }'
'# If it is a file, get the parent directory. If it is a directory, use it directly.'
'$dir = if (Test-Path $rawPath -PathType Leaf) { Split-Path -Parent $rawPath } else { $rawPath }'
'# Strip quotes just in case'
'$dir = $dir -replace ''^"|"$'', '''''
'# Pass directory directly as argument to termote (no Set-Location needed)'
'& "$env:USERPROFILE\.termote-bin\termote.ps1" $dir'
)
Set-Content -Path $termoteFileHandler -Value $handlerLines -Encoding UTF8
# Folder background (right-click in empty space)
$regPath = "HKCU:\Software\Classes\Directory\Background\shell\Termote"
$cmdPath = "$regPath\command"
if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null }
Set-ItemProperty -Path $regPath -Name "(Default)" -Value "Open with Termote"
Set-ItemProperty -Path $regPath -Name "Icon" -Value "`"$shimDir\termote.ps1`",0"
if (-not (Test-Path $cmdPath)) { New-Item -Path $cmdPath -Force | Out-Null }
Set-ItemProperty -Path $cmdPath -Name "(Default)" -Value "powershell -WindowStyle Hidden -File `"$termoteFileHandler`" `"%V`""
# Folder icon (right-click on folder)
$regPath2 = "HKCU:\Software\Classes\Directory\shell\Termote"
$cmdPath2 = "$regPath2\command"
if (-not (Test-Path $regPath2)) { New-Item -Path $regPath2 -Force | Out-Null }
Set-ItemProperty -Path $regPath2 -Name "(Default)" -Value "Open with Termote"
Set-ItemProperty -Path $regPath2 -Name "Icon" -Value "`"$shimDir\termote.ps1`",0"
if (-not (Test-Path $cmdPath2)) { New-Item -Path $cmdPath2 -Force | Out-Null }
Set-ItemProperty -Path $cmdPath2 -Name "(Default)" -Value "powershell -WindowStyle Hidden -File `"$termoteFileHandler`" `"%1`""
Write-Host " Context menu installed." -ForegroundColor Green
# 8. Start termote
Write-Host "[8/8] Starting Termote server..." -ForegroundColor Yellow
Write-Host ""
Write-Host "================================================================" -ForegroundColor Cyan
Write-Host " Installation complete!" -ForegroundColor Green
Write-Host "================================================================" -ForegroundColor Cyan
Write-Host ""
Write-Host " Available commands:" -ForegroundColor White
Write-Host " - termote : Start or connect to Termote" -ForegroundColor Cyan
Write-Host " - termote-kill : Stop all Termote instances" -ForegroundColor Cyan
Write-Host " - termote-link : Show tunnel URL, password & share link" -ForegroundColor Cyan
Write-Host " - termote-log : View real-time backend logs" -ForegroundColor Cyan
Write-Host " - Right-click in folder -> 'Open with Termote'" -ForegroundColor Cyan
Write-Host ""
Write-Host " If commands not found in new terminal, run:" -ForegroundColor Yellow
Write-Host ' $env:PATH = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")' -ForegroundColor Gray
Write-Host ""
# Was: & "$termoteDir\backend\start.ps1"
& "$installDir\start.ps1"