@@ -169,7 +169,7 @@ function Setup-PowerShellPath {
169169
170170 # Windows-specific PATH setup
171171 $currentPath = [Environment ]::GetEnvironmentVariable(" PATH" , " User" )
172-
172+
173173 # Check PATH length limit
174174 $maxPathLength = 2048
175175 if ($currentPath -and ($currentPath.Length + $InstallDir.Length + 1 ) -ge $maxPathLength ) {
@@ -226,6 +226,196 @@ function Setup-PowerShellPath {
226226 return $true
227227}
228228
229+ # Verify SHA256 checksum of a file
230+ function Test-FileChecksum {
231+ param (
232+ [string ]$FilePath ,
233+ [string ]$ExpectedChecksum
234+ )
235+
236+ if (! (Test-Path $FilePath )) {
237+ Write-Error " File not found: $FilePath "
238+ return $false
239+ }
240+
241+ try {
242+ $actualChecksum = (Get-FileHash - Path $FilePath - Algorithm SHA256).Hash.ToLower()
243+ $expectedLower = $ExpectedChecksum.ToLower ()
244+
245+ if ($actualChecksum -eq $expectedLower ) {
246+ return $true
247+ } else {
248+ Write-Error " Checksum verification failed for $FilePath "
249+ Write-Error " Expected: $expectedLower "
250+ Write-Error " Actual: $actualChecksum "
251+ return $false
252+ }
253+ }
254+ catch {
255+ Write-Error " Failed to calculate checksum for $FilePath : $_ "
256+ return $false
257+ }
258+ }
259+
260+ # Download a file with Invoke-WebRequest and verify its checksum
261+ function Invoke-SecureDownload {
262+ param (
263+ [string ]$Url ,
264+ [string ]$OutputPath ,
265+ [string ]$ExpectedChecksum
266+ )
267+
268+ Write-Info " Downloading $ ( Split-Path $OutputPath - Leaf) ..."
269+
270+ try {
271+ # Download with Invoke-WebRequest
272+ Invoke-WebRequest - Uri $Url - OutFile $OutputPath - UseBasicParsing
273+
274+ # Verify checksum
275+ if (Test-FileChecksum - FilePath $OutputPath - ExpectedChecksum $ExpectedChecksum ) {
276+ Write-Success " Downloaded and verified $ ( Split-Path $OutputPath - Leaf) "
277+ return $true
278+ } else {
279+ Remove-Item - Path $OutputPath - Force - ErrorAction SilentlyContinue
280+ return $false
281+ }
282+ }
283+ catch {
284+ Write-Error " Failed to download $Url : $_ "
285+ Remove-Item - Path $OutputPath - Force - ErrorAction SilentlyContinue
286+ return $false
287+ }
288+ }
289+
290+ # Get dependency information
291+ function Get-Dependencies {
292+ return @"
293+ https://storage.googleapis.com/app-releases-production/pieces_cli/release/pieces_cli-1.16.5.tar.gz 3ff92f965dbfe0ffeed9f5460b148e84708e60b39626cd03f3c36a817e78f2ab
294+ https://files.pythonhosted.org/packages/e3/52/6ad8f63ec8da1bf40f96996d25d5b650fdd38f5975f8c813732c47388f18/aenum-3.1.16-py3-none-any.whl 9035092855a98e41b66e3d0998bd7b96280e85ceb3a04cc035636138a1943eaf
295+ https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89
296+ https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz 3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6
297+ https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz 75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b
298+ https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz 27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202
299+ https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz 4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1
300+ https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz 6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8
301+ https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz 75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc
302+ https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz 8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e
303+ https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz 12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9
304+ https://files.pythonhosted.org/packages/d5/00/a297a868e9d0784450faa7365c2172a7d6110c763e30ba861867c32ae6a9/jsonschema-4.25.0.tar.gz e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f
305+ https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz 630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608
306+ https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3
307+ https://files.pythonhosted.org/packages/3a/f5/9506eb5578d5bbe9819ee8ba3198d0ad0e2fbe3bab8b257e4131ceb7dfb6/mcp-1.11.0.tar.gz 49a213df56bb9472ff83b3132a4825f5c8f5b120a90246f08b0dac6bedac44c8
308+ https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
309+ https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz 3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc
310+ https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz 931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed
311+ https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db
312+ https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz 7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc
313+ https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz 06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee
314+ https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz 636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887
315+ https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310
316+ https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz 37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3
317+ https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab
318+ https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz 8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13
319+ https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e
320+ https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa
321+ https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz 439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098
322+ https://files.pythonhosted.org/packages/1e/d9/991a0dee12d9fc53ed027e26a26a64b151d77252ac477e22666b9688bc16/rpds_py-0.27.0.tar.gz 8b23cf252f180cda89220b378d917180f29d313cd6a07b2431c0d3b776aae86f
323+ https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
324+ https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
325+ https://files.pythonhosted.org/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a
326+ https://files.pythonhosted.org/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz 6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8
327+ https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz 38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36
328+ https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz 6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28
329+ https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz 3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760
330+ https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01
331+ https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz 72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5
332+ https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz 3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da
333+ "@
334+ }
335+
336+ # Download and verify all dependencies
337+ function Invoke-DownloadDependencies {
338+ param ([string ]$DownloadDir )
339+
340+ Write-Info " Creating download directory: $DownloadDir "
341+ New-Item - Path $DownloadDir - ItemType Directory - Force | Out-Null
342+
343+ # Parse and download each dependency
344+ $dependencies = Get-Dependencies
345+ $lines = $dependencies -split " `n " | Where-Object { $_.Trim () -ne " " }
346+
347+ foreach ($line in $lines ) {
348+ $parts = $line.Trim () -split " "
349+ if ($parts.Length -ge 2 ) {
350+ $url = $parts [0 ]
351+ $checksum = $parts [1 ]
352+
353+ $filename = Split-Path $url - Leaf
354+ $outputPath = Join-Path $DownloadDir $filename
355+
356+ # Skip if already downloaded and verified
357+ if ((Test-Path $outputPath ) -and (Test-FileChecksum - FilePath $outputPath - ExpectedChecksum $checksum )) {
358+ Write-Info " Already have verified $filename "
359+ continue
360+ }
361+
362+ # Download and verify
363+ if (! (Invoke-SecureDownload - Url $url - OutputPath $outputPath - ExpectedChecksum $checksum )) {
364+ Write-Error " Failed to download and verify $filename "
365+ return $false
366+ }
367+ }
368+ }
369+
370+ Write-Success " All dependencies downloaded and verified!"
371+ return $true
372+ }
373+
374+ # Install packages offline using pip with no-deps and local files
375+ function Install-PackagesOffline {
376+ param (
377+ [string ]$DownloadDir ,
378+ [string ]$PipPath
379+ )
380+
381+ Write-Info " Installing packages from verified downloads..."
382+
383+ # Parse and install each dependency
384+ $dependencies = Get-Dependencies
385+ $lines = $dependencies -split " `n " | Where-Object { $_.Trim () -ne " " }
386+
387+ foreach ($line in $lines ) {
388+ $parts = $line.Trim () -split " "
389+ if ($parts.Length -ge 2 ) {
390+ $url = $parts [0 ]
391+ $filename = Split-Path $url - Leaf
392+ $packagePath = Join-Path $DownloadDir $filename
393+
394+ if (! (Test-Path $packagePath )) {
395+ Write-Error " Package file not found: $packagePath "
396+ return $false
397+ }
398+
399+ Write-Info " Installing $filename ..."
400+
401+ # Install with no dependencies flag to prevent pip from accessing PyPI
402+ try {
403+ & $PipPath install $packagePath -- no- deps -- force- reinstall -- quiet
404+ if ($LASTEXITCODE -ne 0 ) {
405+ throw " pip install failed with exit code $LASTEXITCODE "
406+ }
407+ }
408+ catch {
409+ Write-Error " Failed to install $filename : $_ "
410+ return $false
411+ }
412+ }
413+ }
414+
415+ Write-Success " All packages installed successfully!"
416+ return $true
417+ }
418+
229419# Check if running as admin/root
230420function Test-Administrator {
231421 if (Test-Windows ) {
@@ -246,7 +436,22 @@ function Test-Administrator {
246436function Install-PiecesCLI {
247437 Write-Info " Starting Pieces CLI installation..."
248438
249- # Step 1: Check if running as Administrator/root
439+ # Step 1: Check system requirements
440+ Write-Info " Checking system requirements..."
441+
442+ # PowerShell should have Invoke-WebRequest and Get-FileHash built-in
443+ # These are required for secure downloads and checksum verification
444+ try {
445+ Get-Command Invoke-WebRequest - ErrorAction Stop | Out-Null
446+ Get-Command Get-FileHash - ErrorAction Stop | Out-Null
447+ }
448+ catch {
449+ Write-Error " Required PowerShell cmdlets not available (Invoke-WebRequest, Get-FileHash)"
450+ Write-Error " Please ensure you're running PowerShell 3.0 or later"
451+ return
452+ }
453+
454+ # Step 2: Check if running as Administrator/root
250455 if (Test-Administrator ) {
251456 Write-Warning " You appear to be running this script as Administrator/root."
252457 Write-Warning " This may cause the installation to be inaccessible to non-admin users."
@@ -257,7 +462,7 @@ function Install-PiecesCLI {
257462 }
258463 }
259464
260- # Step 2 : Find Python executable
465+ # Step 3 : Find Python executable
261466 Write-Info " Locating Python executable..."
262467 $pythonCmd = Find-Python
263468
@@ -274,7 +479,7 @@ function Install-PiecesCLI {
274479 $pythonVersion = & $pythonCmd.Split (' ' ) -- version 2>&1
275480 Write-Success " Found Python: $pythonCmd ($pythonVersion )"
276481
277- # Step 3 : Set installation directory
482+ # Step 4 : Set installation directory
278483 $homeDir = Get-HomeDirectory
279484 $installDir = Join-Path $homeDir " .pieces-cli"
280485 $venvDir = Join-Path $installDir " venv"
@@ -286,7 +491,7 @@ function Install-PiecesCLI {
286491 New-Item - Path $installDir - ItemType Directory | Out-Null
287492 }
288493
289- # Step 4 : Create virtual environment
494+ # Step 5 : Create virtual environment
290495 Write-Info " Creating virtual environment..."
291496 if (Test-Path $venvDir ) {
292497 Write-Warning " Virtual environment already exists. Removing old environment..."
@@ -313,9 +518,6 @@ function Install-PiecesCLI {
313518
314519 Write-Success " Virtual environment created successfully."
315520
316- # Step 5: Install pieces-cli
317- Write-Info " Installing Pieces CLI..."
318-
319521 # Use venv's pip - different paths for Windows vs Unix
320522 if (Test-Windows ) {
321523 $venvPip = Join-Path $venvDir " Scripts\pip.exe"
@@ -330,32 +532,29 @@ function Install-PiecesCLI {
330532 return
331533 }
332534
333- # Upgrade pip first
334- Write-Info " Upgrading pip..."
335- try {
336- & $venvPip install -- upgrade pip -- quiet
337- if ($LASTEXITCODE -ne 0 ) { throw " Pip upgrade failed" }
338- }
339- catch {
340- Write-Warning " Failed to upgrade pip, continuing with existing version..."
341- }
535+ # Step 6a: Download all dependencies securely
536+ $downloadDir = Join-Path $installDir " downloads"
537+ Write-Info " Downloading and verifying all dependencies"
342538
343- # Install pieces-cli
344- Write-Info " Installing pieces-cli package..."
345- try {
346- & $venvPip install pieces- cli -- quiet
347- if ($LASTEXITCODE -ne 0 ) { throw " pieces-cli installation failed" }
348- }
349- catch {
350- Write-Error " Failed to install pieces-cli: $_ "
539+ if (! (Invoke-DownloadDependencies - DownloadDir $downloadDir )) {
540+ Write-Error " Failed to download dependencies."
351541 Write-Error " Please check your internet connection and try again."
352- Write-Error " If the problem persists, check if pypi.org is accessible."
542+ return
543+ }
544+
545+ if (! (Install-PackagesOffline - DownloadDir $downloadDir - PipPath $venvPip )) {
546+ Write-Error " Failed to install packages offline."
547+ Write-Error " Installation may be corrupted, please try again."
353548 return
354549 }
355550
356551 Write-Success " Pieces CLI installed successfully!"
357552
358- # Step 6: Create wrapper script
553+ # Clean up downloads after successful installation
554+ Write-Info " Cleaning up download cache..."
555+ Remove-Item - Path $downloadDir - Recurse - Force - ErrorAction SilentlyContinue
556+
557+ # Step 7: Create wrapper script
359558 Write-Info " Creating wrapper script..."
360559
361560 if (Test-Windows ) {
@@ -442,7 +641,7 @@ exec "`$PIECES_EXECUTABLE" "`$@"
442641
443642 Write-Success " Wrapper script created at: $wrapperScript "
444643
445- # Step 7 : Configure PowerShell
644+ # Step 8 : Configure PowerShell
446645 Write-Info " Configuring PowerShell integration..."
447646
448647 if (Test-Command " pwsh" ) {
@@ -478,7 +677,7 @@ exec "`$PIECES_EXECUTABLE" "`$@"
478677 Write-Host " "
479678 }
480679
481- # Step 8 : Final instructions
680+ # Step 9 : Final instructions
482681 Write-Host " "
483682 Write-Success " Installation completed successfully!"
484683 Write-Host " "
0 commit comments