From 225fb2cc75a8929ffe9310f8be417d9c1b098933 Mon Sep 17 00:00:00 2001 From: Kevin Hou Date: Thu, 21 May 2026 14:23:58 -0700 Subject: [PATCH 1/3] Fix spelling mistake in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b735dedab..3edfb3a12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ The terminal-first surface to interact with Antigravity agents. Stay in your flo - Moves the **terminal** color scheme to the top of the selection list, making it the default choice during onboarding and in `/settings`. - Improved `/usage` and `/quota` commands. Forces a real-time reload of model configuration and remaining quotas, allowing you to see updated real-time consumption statistics immediately. - Improved step rendering layout. Calculates available terminal width dynamically and uses middle-truncation (`/foo/.../bar`) for file path tools to prevent layout shifting on narrow screens. -- Improved session deletion keybinding in `/reusme`. Changed the shortcut from `ctrl+d` to `ctrl+delete` to resolve conflicts with the global exit keybinding (`ctrl+d` `ctrl+d`) and preserve Emacs-style forward-delete in search input fields. +- Improved session deletion keybinding in `/resume`. Changed the shortcut from `ctrl+d` to `ctrl+delete` to resolve conflicts with the global exit keybinding (`ctrl+d` `ctrl+d`) and preserve Emacs-style forward-delete in search input fields. - Restored automatic table wrapping, preventing long cells inside markdown tables from being truncated. - Resolved an issue where deleted files (represented by `+++ /dev/null`) had their deletion lines incorrectly merged into the previous file's diff. From ecfabdd9155e38fa4c5bf97da53e164b1acea363 Mon Sep 17 00:00:00 2001 From: Shengzhe Yao Date: Sat, 23 May 2026 11:22:33 -0700 Subject: [PATCH 2/3] add version 1.0.2 release notes --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3edfb3a12..87dde4be8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ The terminal-first surface to interact with Antigravity agents. Stay in your flow without context switching. +## 1.0.2 + +- Added `AGY_CLI_HIDE_ACCOUNT_INFO` environment variable to hide email and plan tier from the header. +- Fixed timeout overrides: restricted the default 60-second interaction timeout specifically to subagents, preventing the main agent from being unconditionally capped. +- Fixed a nil-pointer panic in Sandbox Mode: resolved a typed nil interface comparison when fetching URL content. +- Fixed fallback skill discovery in Standalone mode: ensures custom/fallback skills are successfully loaded even if the standard configuration directory is missing, and added automatic path deduplication to prevent duplicates. +- Fixed command rendering in message history: prefixed slash commands with a caret (`>`) in response block headers to clearly distinguish user-typed commands from agent outputs. +- Fixed plugin installation path mismatch: updated the `plugin` subcommand to install downloaded plugins directly to the shared configuration directory (`~/.gemini/config/`) rather than the private application data folder, making them instantly discoverable. +- Fixed Git short-hash support in diff selection: updated the commit hash recognition pattern in the /diff commit selection tree to match Git's standard 7-character short hashes (and up to 40-character full hashes). +- Fixed statusline subcommand handling and recursive loops: added case-insensitive subcommand parsing (help, delete, reset, enable/on, disable/off) to the /statusline command, providing direct control to toggle or revert custom statuslines and blocking recursive shell hangs during help queries. +- Improved `/help` shortcuts tab by sorting shortcuts by keybinding key, adding missing keybindings (like `ctrl+r`, `ctrl+o`, `alt+j`, `ctrl+k`), and generalizing scrolling (PageUp/PageDown/GoToTop/GoToBottom) for both Commands and Shortcuts tabs. + ## 1.0.1 - Fixed OAuth token persistence and authentication hangs. From c9e23b2ec9347f004988c8d8900351dfbcd3e406 Mon Sep 17 00:00:00 2001 From: weby-homelab Date: Wed, 27 May 2026 16:31:54 +0300 Subject: [PATCH 3/3] feat: add installation scripts, Makefile, and config template - Add install.sh (Linux/macOS), install.ps1 (PowerShell), and install.cmd (Windows CMD) to make official install scripts accessible inside the repository - Add Makefile for quick lifespan management (install/check/uninstall) - Add .antigravity.md project context template - Implement a backward-compatible offline/local fallback check in all installers (which activates if packages/ manifests and binaries are placed locally in the repo) --- .antigravity.md | 23 ++++ Makefile | 66 ++++++++++++ install.cmd | 241 ++++++++++++++++++++++++++++++++++++++++++ install.ps1 | 234 +++++++++++++++++++++++++++++++++++++++++ install.sh | 273 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 837 insertions(+) create mode 100644 .antigravity.md create mode 100644 Makefile create mode 100644 install.cmd create mode 100644 install.ps1 create mode 100755 install.sh diff --git a/.antigravity.md b/.antigravity.md new file mode 100644 index 000000000..085a26aff --- /dev/null +++ b/.antigravity.md @@ -0,0 +1,23 @@ +# Project Configuration for Antigravity CLI +# Place this file in the root of your project to provide context to the AI agent. +# Docs: https://antigravity.google/docs/cli-overview + +# Project Overview +# Describe your project, its purpose, and key technologies. +# Example: +# +# This is a Python FastAPI backend with PostgreSQL. +# - Use Pydantic v2 (model_dump(), not dict()) +# - All endpoints must have OpenAPI descriptions +# - Tests: pytest with async fixtures +# - Docker: compose v2 format + +# Coding Standards +# - Follow PEP8 / ESLint / your linter rules +# - Use meaningful commit messages (conventional commits) +# - All functions must have docstrings/JSDoc + +# Security +# - Never hardcode credentials — use environment variables +# - Validate all user input +# - Use parameterized queries for SQL diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..8418945dd --- /dev/null +++ b/Makefile @@ -0,0 +1,66 @@ +.PHONY: install update uninstall check help + +BINARY_NAME := agy +INSTALL_DIR := $(HOME)/.local/bin +BINARY_PATH := $(INSTALL_DIR)/$(BINARY_NAME) + +help: ## Show this help + @echo "Antigravity CLI — Makefile targets:" + @echo "" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}' + @echo "" + +install: ## Install Antigravity CLI via official script + @if [ -f "$(BINARY_PATH)" ]; then \ + echo "✓ agy is already installed at $(BINARY_PATH)"; \ + echo " Run 'make update' to update, or 'make reinstall' for fresh install."; \ + else \ + echo "⠋ Installing Antigravity CLI..."; \ + bash install.sh; \ + echo ""; \ + echo "✓ Installation complete! Run 'agy' to start."; \ + fi + +reinstall: uninstall install ## Remove and reinstall + +update: ## Update to the latest version + @if [ -f "$(BINARY_PATH)" ]; then \ + echo "⠋ Updating Antigravity CLI..."; \ + $(BINARY_PATH) update; \ + else \ + echo "✗ agy is not installed. Run 'make install' first."; \ + exit 1; \ + fi + +uninstall: ## Remove Antigravity CLI binary + @if [ -f "$(BINARY_PATH)" ]; then \ + rm -f "$(BINARY_PATH)"; \ + echo "✓ Removed $(BINARY_PATH)"; \ + else \ + echo "✓ agy is not installed at $(BINARY_PATH)"; \ + fi + +check: ## Check if agy is installed and show version + @if command -v agy >/dev/null 2>&1; then \ + echo "✓ agy found: $$(command -v agy)"; \ + echo " Version: $$(agy --version 2>/dev/null || echo 'unknown')"; \ + elif [ -f "$(BINARY_PATH)" ]; then \ + echo "✓ agy found at $(BINARY_PATH) (not in PATH)"; \ + echo " Version: $$($(BINARY_PATH) --version 2>/dev/null || echo 'unknown')"; \ + echo ""; \ + echo " Add to PATH:"; \ + echo " export PATH=\"$(INSTALL_DIR):\$$PATH\""; \ + else \ + echo "✗ agy is not installed. Run 'make install'."; \ + exit 1; \ + fi + +run: ## Start agy interactive session + @if command -v agy >/dev/null 2>&1; then \ + agy; \ + elif [ -f "$(BINARY_PATH)" ]; then \ + $(BINARY_PATH); \ + else \ + echo "✗ agy is not installed. Run 'make install' first."; \ + exit 1; \ + fi diff --git a/install.cmd b/install.cmd new file mode 100644 index 000000000..e273dd8ce --- /dev/null +++ b/install.cmd @@ -0,0 +1,241 @@ +@echo off +setlocal enabledelayedexpansion +:: Absolute early argument sanitization to prevent RCE/LCE metacharacter injections safely +set "CMD_LINE=!CMDCMDLINE!" +echo(!CMD_LINE! | findstr /c:"&" /c:"|" /c:";" /c:"<" /c:">" /c:"^" >nul 2>&1 +if !ERRORLEVEL! equ 0 ( + echo Fatal: Illegal shell characters detected in command line arguments. >&2 + exit /b 1 +) +endlocal +setlocal disabledelayedexpansion + +set "DOWNLOAD_BASE_URL=https://antigravity-cli-auto-updater-974169037036.us-central1.run.app" +set "CUSTOM_DIR=" +set "FORWARD_ARGS=" + +:parse_args +if "%~1" == "" goto :args_done +if "%~1" == "-d" goto :handle_d +if "%~1" == "--dir" goto :handle_d +:: Accumulate other arguments to forward. Delayed expansion is disabled here, +:: so we must use standard percent-style expansion. +set "FORWARD_ARGS=%FORWARD_ARGS% %1" +shift +goto :parse_args + +:handle_d +if "%~2" == "" ( + echo Error: Missing value for %~1 option. >&2 + exit /b 1 +) +set "CUSTOM_DIR=%~2" +shift & shift +goto :parse_args + +:args_done +setlocal enabledelayedexpansion +:: Use standard LOCALAPPDATA variable +set "TARGET_DIR=!LOCALAPPDATA!\agy\bin" +if not "!CUSTOM_DIR!" == "" set "TARGET_DIR=!CUSTOM_DIR!" +set "BINARY_PATH=!TARGET_DIR!\agy.exe" + +REM Pre-existence & Dynamic Path Check +if exist "!BINARY_PATH!" ( + echo Notice: 'agy.exe' is already installed at !BINARY_PATH!. + echo The Antigravity CLI automatically self-updates in the background. + echo If you want to perform a fresh installation, delete the binary first: + echo del "!BINARY_PATH!" + exit /b 0 +) + +REM 1. Detect Architecture +if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( + set "PLATFORM=windows_amd64" +) else if /i "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( + set "PLATFORM=windows_arm64" +) else ( + echo Fatal: Unsupported CPU architecture. >&2 + exit /b 1 +) + +REM 2. Query Manifest & Parse securely via pure-CMD +set "LOCAL_MODE=false" +set "SCRIPT_DIR=%~dp0" +set "LOCAL_MANIFEST=!SCRIPT_DIR!packages\manifests\!PLATFORM!.json" + +if exist "!LOCAL_MANIFEST!" ( + set "MANIFEST_PATH=!LOCAL_MANIFEST!" + set "LOCAL_MODE=true" +) else ( + set "MANIFEST_URL=!DOWNLOAD_BASE_URL!/manifests/!PLATFORM!.json" + set "MANIFEST_PATH=!TEMP!\manifest_!RANDOM!.json" + curl -fsSL "!MANIFEST_URL!" -o "!MANIFEST_PATH!" + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to download release manifest from !MANIFEST_URL!. >&2 + echo Please check your internet connection or firewall settings. >&2 + if exist "!MANIFEST_PATH!" del "!MANIFEST_PATH!" + exit /b 1 + ) +) + +:: Securely extract manifest values line-by-line (immune to minified JSON and spaces) +set "VER_FILE=!TEMP!\ver_!RANDOM!.txt" +findstr /c:"\"version\":" "!MANIFEST_PATH!" > "!VER_FILE!" 2>nul +set "RAW_LINE=" +set /p RAW_LINE=<"!VER_FILE!" & del "!VER_FILE!" +set "VERSION=" +if defined RAW_LINE ( + set "CLEAN_LINE=!RAW_LINE: =!" + set "CLEAN_LINE=!CLEAN_LINE:*"version":"=!" + set "CLEAN_LINE=!CLEAN_LINE:"= !" + for /f "tokens=1" %%A in ("!CLEAN_LINE!") do set "VERSION=%%A" + set "VERSION=!VERSION:,=!" +) + +set "URL_FILE=!TEMP!\url_!RANDOM!.txt" +findstr /c:"\"url\":" "!MANIFEST_PATH!" > "!URL_FILE!" 2>nul +set "RAW_LINE=" +set /p RAW_LINE=<"!URL_FILE!" & del "!URL_FILE!" +set "URL=" +if defined RAW_LINE ( + set "CLEAN_LINE=!RAW_LINE: =!" + set "CLEAN_LINE=!CLEAN_LINE:*"url":"=!" + set "CLEAN_LINE=!CLEAN_LINE:"= !" + for /f "tokens=1" %%A in ("!CLEAN_LINE!") do set "URL=%%A" + set "URL=!URL:,=!" +) + +set "SHA_FILE=!TEMP!\sha_!RANDOM!.txt" +findstr /c:"\"sha512\":" "!MANIFEST_PATH!" > "!SHA_FILE!" 2>nul +set "RAW_LINE=" +set /p RAW_LINE=<"!SHA_FILE!" & del "!SHA_FILE!" +set "SHA512=" +if defined RAW_LINE ( + set "CLEAN_LINE=!RAW_LINE: =!" + set "CLEAN_LINE=!CLEAN_LINE:*"sha512":"=!" + set "CLEAN_LINE=!CLEAN_LINE:"= !" + for /f "tokens=1" %%A in ("!CLEAN_LINE!") do set "SHA512=%%A" + set "SHA512=!SHA512:,=!" +) + +if not "!LOCAL_MODE!"=="true" ( + del "!MANIFEST_PATH!" +) + +if "!URL!"=="" ( + echo Fatal: Failed to download or parse release manifest. >&2 + exit /b 1 +) + +if "!LOCAL_MODE!"=="true" ( + for /f "delims=" %%F in ("!URL!") do set "ARCHIVE_NAME=%%~nxF" + if /i "!ARCHIVE_NAME!"=="cli_windows_x64.exe" set "ARCHIVE_NAME=cli_windows_x64.zip" + set "LOCAL_ARCHIVE=!SCRIPT_DIR!packages\binaries\!ARCHIVE_NAME!" + if not exist "!LOCAL_ARCHIVE!" ( + echo Fatal: Local archive !LOCAL_ARCHIVE! not found. >&2 + exit /b 1 + ) + echo ✓ Local package files found for platform !PLATFORM!. Installing offline... +) + +REM 3. Download/Stage & Verify Checksum +set "STAGING_DIR=!LOCALAPPDATA!\antigravity\staging" +if not exist "!STAGING_DIR!" mkdir "!STAGING_DIR!" +if not exist "!STAGING_DIR!" ( + echo Fatal: Failed to create staging directory !STAGING_DIR!. >&2 + echo Please check write permissions for !LOCALAPPDATA!. >&2 + exit /b 1 +) + +set "IS_ZIP=false" +if /i "!URL:~-4!"==".zip" set "IS_ZIP=true" +if defined LOCAL_ARCHIVE ( + if /i "!LOCAL_ARCHIVE:~-4!"==".zip" set "IS_ZIP=true" +) + +if "!IS_ZIP!"=="true" ( + set "STAGING_PAYLOAD=!STAGING_DIR!\agy.zip" +) else ( + set "STAGING_PAYLOAD=!STAGING_DIR!\agy.exe" +) + +if "!LOCAL_MODE!"=="true" ( + echo ⠋ Staging local release package... + copy /y "!LOCAL_ARCHIVE!" "!STAGING_PAYLOAD!" >nul +) else ( + echo ⠋ Downloading release package... + curl -fsSL "!URL!" -o "!STAGING_PAYLOAD!" + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to download release binary from !URL!. >&2 + echo Please check your internet connection or firewall settings. >&2 + if exist "!STAGING_PAYLOAD!" del "!STAGING_PAYLOAD!" + exit /b 1 + ) +) + +if "!SHA512!"=="" ( + echo Security Halt: Checksum missing in manifest. >&2 + del "!STAGING_PAYLOAD!" & exit /b 1 +) + +set "ACTUAL=" +for /f "usebackq skip=1 tokens=*" %%i in (`certutil -hashfile "!STAGING_PAYLOAD!" SHA512 2^>nul`) do ( + if not defined ACTUAL ( + set "ACTUAL=%%i" & set "ACTUAL=!ACTUAL: =!" + ) +) +if /i not "!ACTUAL!"=="!SHA512!" ( + echo Security Halt: Checksum verification failed. The file may be corrupted or compromised. >&2 + del "!STAGING_PAYLOAD!" & exit /b 1 +) +echo ✓ Release package checksum verified. + +REM 4. Place Binary & Unblock +if not exist "!TARGET_DIR!" mkdir "!TARGET_DIR!" +if not exist "!TARGET_DIR!" ( + echo Fatal: Failed to create target directory !TARGET_DIR!. >&2 + echo Please check directory permissions or specify a writable custom directory with --dir. >&2 + del "!STAGING_PAYLOAD!" + exit /b 1 +) + +if "!IS_ZIP!"=="true" ( + echo ⠋ Extracting binary from ZIP archive... + powershell -NoProfile -Command "Expand-Archive -Path '!STAGING_PAYLOAD!' -DestinationPath '!STAGING_DIR!' -Force" + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to extract ZIP package. >&2 + del "!STAGING_PAYLOAD!" + exit /b 1 + ) + copy /y "!STAGING_DIR!\cli_windows_x64.exe" "!BINARY_PATH!" >nul + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to copy extracted binary. >&2 + del "!STAGING_PAYLOAD!" + exit /b 1 + ) + del "!STAGING_DIR!\cli_windows_x64.exe" >nul 2>&1 +) else ( + copy /y "!STAGING_PAYLOAD!" "!BINARY_PATH!" >nul + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to write binary to destination at !BINARY_PATH!. >&2 + echo Please check directory permissions or if the file is locked, e.g. if 'agy.exe' is currently running. >&2 + del "!STAGING_PAYLOAD!" + exit /b 1 + ) +) +ping -n 2 127.0.0.1 >nul 2>&1 +del "!STAGING_PAYLOAD!" +del "!BINARY_PATH!:Zone.Identifier" >nul 2>&1 + +REM 5. Go-Native Setup Handoff with Passthrough Flags +set "SETUP_FLAGS=" +if not "!CUSTOM_DIR!"=="" set "SETUP_FLAGS=--dir "!CUSTOM_DIR!"" +set "SETUP_FLAGS=!SETUP_FLAGS! !FORWARD_ARGS!" + +:: Handoff safely via immune loop variables to prevent delayed expansion path mangling on '!' +for /f "tokens=1* delims=?" %%A in ("!BINARY_PATH!?!SETUP_FLAGS!") do ( + endlocal & endlocal + "%%A" install %%B +) +exit /b 0 diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 000000000..41726a448 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,234 @@ +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest +if ($ExecutionContext.SessionState.LanguageMode -ne 'ConstrainedLanguage') { + try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + } catch { + # Ignore failure, TLS 1.2 might already be default or we can't set it. + } +} + +$hasPath = ($null -ne $MyInvocation.MyCommand) -and ($null -ne $MyInvocation.MyCommand.PSObject.Properties['Path']) +$scriptPath = if ($hasPath) { $MyInvocation.MyCommand.Path } else { $null } +$isSourced = [string]::IsNullOrEmpty($scriptPath) -or ($MyInvocation.InvocationName -eq '.') + +$stagingPayload = $null +$script:installExitCode = 0 + +function Invoke-Install { + param($ScriptArgs) + + $DOWNLOAD_BASE_URL = "https://antigravity-cli-auto-updater-974169037036.us-central1.run.app" + $TARGET_DIR = Join-Path $env:LOCALAPPDATA "agy\bin" + $CUSTOM_DIR = "" + $passthroughArgs = @() + + # Secure loop argument parsing and forward gathering + for ($i = 0; $i -lt $ScriptArgs.Count; $i++) { + if ($ScriptArgs[$i] -eq "-d" -or $ScriptArgs[$i] -eq "--dir") { + if ($i + 1 -lt $ScriptArgs.Count) { + $CUSTOM_DIR = $ScriptArgs[$i + 1] + $i++ + } else { + Write-Error "Error: Missing value for directory option." -ErrorAction Continue + if ($isSourced) { throw "Error: Missing value for directory option." } else { $script:installExitCode = 1; return } + } + } else { + $passthroughArgs += $ScriptArgs[$i] + } + } + if ($CUSTOM_DIR -ne "") { $TARGET_DIR = $CUSTOM_DIR } + $binaryPath = Join-Path $TARGET_DIR "agy.exe" + + # Pre-existence & Dynamic Path Check + if (Test-Path $binaryPath) { + Write-Host "Notice: 'agy.exe' is already installed at $binaryPath." + Write-Host "The Antigravity CLI automatically self-updates in the background." + Write-Host "If you want to perform a fresh installation, delete the binary first:" + Write-Host " Remove-Item `"$binaryPath`" -Force" + $script:installExitCode = 0; return + } + + # 1. Detect Architecture + $arch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } + if ($arch -eq "AMD64") { + $platform = "windows_amd64" + } elseif ($arch -eq "ARM64") { + $platform = "windows_arm64" + } else { + Write-Error "Fatal: Unsupported CPU architecture." -ErrorAction Continue + if ($isSourced) { throw "Fatal: Unsupported CPU architecture." } else { $script:installExitCode = 1; return } + } + + # 2. Query Manifest (Local or Remote) + $localMode = $false + $scriptDir = $null + if ($hasPath) { + $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path + } + if ([string]::IsNullOrEmpty($scriptDir) -and (Test-Path "Variable:PSScriptRoot")) { + $scriptDir = $PSScriptRoot + } + if ([string]::IsNullOrEmpty($scriptDir)) { + $scriptDir = Get-Location + } + + $localManifest = Join-Path $scriptDir "packages\manifests\$platform.json" + if (Test-Path $localManifest) { + try { + $manifestContent = Get-Content -Raw -Path $localManifest + # Simple regex parser to avoid ConvertFrom-Json dependency/failure issues + $version = if ($manifestContent -match '"version"\s*:\s*"([^"]+)"') { $Matches[1] } else { "" } + $url = if ($manifestContent -match '"url"\s*:\s*"([^"]+)"') { $Matches[1] } else { "" } + $sha512 = if ($manifestContent -match '"sha512"\s*:\s*"([^"]+)"') { $Matches[1] } else { "" } + + if ($url -ne "" -and $sha512 -ne "") { + $archiveFilename = Split-Path -Leaf $url + $localArchive = Join-Path $scriptDir "packages\binaries\$archiveFilename" + if (Test-Path $localArchive) { + $localMode = $true + Write-Host "✓ Local package files found for platform $platform. Installing offline..." + } + } + } catch { + # Ignore and fallback + } + } + + if (-not $localMode) { + Write-Host "⠋ Querying release repository..." + try { + $manifest = Invoke-RestMethod -Uri "$DOWNLOAD_BASE_URL/manifests/$platform.json" + $version = $manifest.version + $url = $manifest.url + $sha512 = $manifest.sha512 + } catch { + Write-Error "Fatal: Failed to download release manifest from $DOWNLOAD_BASE_URL/manifests/$platform.json. Network or DNS issue?" -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to download manifest." } else { $script:installExitCode = 1; return } + } + } + + Write-Host "✓ Latest available version: $version" + + # 3. Download/Stage Binary & Verify Checksum + $stagingDir = Join-Path $env:LOCALAPPDATA "antigravity\staging" + try { + if (-not (Test-Path $stagingDir)) { New-Item -ItemType Directory -Path $stagingDir | Out-Null } + } catch { + Write-Error "Fatal: Failed to create staging directory at $stagingDir. Please check write permissions." -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to create staging directory." } else { $script:installExitCode = 1; return } + } + + $isZip = $url.EndsWith(".zip") + $payloadName = if ($isZip) { "agy.zip" } else { "agy.exe" } + $localStagingPayload = Join-Path $stagingDir $payloadName + $script:stagingPayload = $localStagingPayload + + if ($localMode) { + Write-Host "⠋ Staging local release package..." + try { + Copy-Item -Path $localArchive -Destination $localStagingPayload -Force + } catch { + Write-Error "Fatal: Failed to copy local archive from $localArchive to $localStagingPayload." -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to copy local archive." } else { $script:installExitCode = 1; return } + } + } else { + Write-Host "⠋ Downloading release package..." + $ProgressPreference = 'SilentlyContinue' + try { + Invoke-WebRequest -Uri $url -OutFile $localStagingPayload + } catch { + Write-Error "Fatal: Failed to download binary from $url. Network or DNS issue?" -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to download binary." } else { $script:installExitCode = 1; return } + } + } + $hash = $null + if ($ExecutionContext.SessionState.LanguageMode -ne 'ConstrainedLanguage') { + try { + $hash = (Get-FileHash $localStagingPayload -Algorithm SHA512).Hash.ToLower() + } catch { + # Ignore and fallback + } + } + if ($null -eq $hash) { + try { + $certutilOut = certutil -hashfile $localStagingPayload SHA512 + if ($LASTEXITCODE -eq 0 -and $certutilOut.Count -ge 2) { + $hash = ($certutilOut[1] -replace '\s').ToLower() + } + } catch { + # Ignore + } + } + if ($null -eq $hash) { + Write-Error "Fatal: Failed to compute file hash for verification." -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to compute file hash." } else { $script:installExitCode = 1; return } + } + if ($hash -ne $sha512.ToLower()) { + Write-Error "Security Halt: Checksum verification failed. The downloaded file may be corrupted or compromised." -ErrorAction Continue + if ($isSourced) { throw "Checksum verification failed." } else { $script:installExitCode = 1; return } + } + + # 4. Place Binary & Unblock + try { + if (-not (Test-Path $TARGET_DIR)) { New-Item -ItemType Directory -Path $TARGET_DIR | Out-Null } + if ($isZip) { + Write-Host "⠋ Extracting binary from ZIP archive..." + Expand-Archive -Path $localStagingPayload -DestinationPath $stagingDir -Force + $extractedBinary = Join-Path $stagingDir "cli_windows_x64.exe" + Copy-Item -Path $extractedBinary -Destination $binaryPath -Force + Remove-Item -Path $extractedBinary -Force -ErrorAction SilentlyContinue + } else { + Copy-Item -Path $localStagingPayload -Destination $binaryPath -Force + } + Unblock-File -Path $binaryPath -ErrorAction SilentlyContinue + } catch { + Write-Error "Write Error: Permission denied or failed to write binary to $binaryPath." -ErrorAction Continue + Write-Error "Please check directory permissions or if the file is locked (e.g. if 'agy.exe' is currently running)." -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to install binary." } else { $script:installExitCode = 1; return } + } + + # 5. Go-Native Setup Handoff with Passthrough Flags + $setupFlags = @() + if ($CUSTOM_DIR -ne "") { + $setupFlags += "--dir" + $setupFlags += $CUSTOM_DIR + } + $setupFlags += $passthroughArgs + + # Execute native environment setup (absorb non-fatal soft warning exits) + try { + & $binaryPath install $setupFlags + } catch { + # Absorb setup warnings/failures to align with Unix '|| true'. + # The binary is successfully copied and functional on disk. + } + + $script:installExitCode = 0 + return +} + +$script:installExitCode = 0 +try { + Invoke-Install $args +} finally { + # Guaranteed staging cleanup on success or exit error + if ($null -ne $stagingPayload -and (Test-Path $stagingPayload)) { + Remove-Item $stagingPayload -Force + } +} +$exitCode = $script:installExitCode + +if ($exitCode -ne 0) { + if ($isSourced) { + throw "Fatal: Installation failed." + } else { + exit $exitCode + } +} + diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..a89b43603 --- /dev/null +++ b/install.sh @@ -0,0 +1,273 @@ +#!/bin/bash +# +# Antigravity CLI - Unix Bootstrapper Script (Bash/Zsh/Fish) +# +# Downloads, staging-verifies, and installs the Antigravity CLI flat native build. +# + +set -euo pipefail + +# 1. Default Setup & Constants +DOWNLOAD_BASE_URL="https://antigravity-cli-auto-updater-974169037036.us-central1.run.app" +TARGET_DIR="$HOME/.local/bin" +CUSTOM_DIR="" + +# Helper: Display usage instructions +show_usage() { + echo "Usage: install.sh [options]" + echo "" + echo "Options:" + echo " -d, --dir Specify a custom directory to install the binary" + echo " -h, --help Display this help menu" + echo "" +} + +# Parse Arguments +while [ "$#" -gt 0 ]; do + case $1 in + -d|--dir) + if [ -z "${2:-}" ]; then + echo "[ERROR] Missing path for --dir parameter" >&2 + exit 1 + fi + CUSTOM_DIR="$2" + shift + ;; + -h|--help) + show_usage + exit 0 + ;; + *) + echo "[ERROR] Unknown parameter: $1" >&2 + show_usage + exit 1 + ;; + esac + shift +done + +# Resolve dynamic installation target directory +if [ -n "$CUSTOM_DIR" ]; then + TARGET_DIR="$CUSTOM_DIR" +fi + +BINARY_PATH="$TARGET_DIR/agy" + +# 2. STEP 1: Pre-existence & Dynamic Path Check +if [ -f "$BINARY_PATH" ]; then + echo "Notice: 'agy' is already installed at $BINARY_PATH." + echo "The Antigravity CLI automatically self-updates in the background during regular runs." + echo "" + echo "If you want to perform a fresh installation, delete the binary first:" + echo " rm \"$BINARY_PATH\"" + exit 0 +fi + +echo "⠋ Detecting system environment..." + +# 3. Detect Platform +case "$(uname -s)" in + Darwin) os="darwin" ;; + Linux) os="linux" ;; + *) echo "Fatal: Unsupported operating system: $(uname -s). Antigravity CLI currently supports 64-bit Windows, macOS, and Linux." >&2; exit 1 ;; +esac + +case "$(uname -m)" in + x86_64|amd64) arch="amd64" ;; + arm64|aarch64) arch="arm64" ;; + *) echo "Fatal: Unsupported architecture: $(uname -m). Antigravity CLI currently supports 64-bit Windows, macOS, and Linux." >&2; exit 1 ;; +esac + +# musl libc detection on Linux +platform="" +if [ "$os" = "linux" ]; then + if [ -f /lib/libc.musl-x86_64.so.1 ] || [ -f /lib/libc.musl-aarch64.so.1 ] || ldd /bin/ls 2>&1 | grep -q musl; then + platform="linux_${arch}_musl" + else + platform="linux_${arch}" + fi +else + platform="${os}_${arch}" +fi + +echo "✓ Platform detected: $platform" + +# POSIX-compliant JSON parser (no jq or grep -o dependencies) +parse_json_key() { + local payload="$1" + local key="$2" + echo "$payload" | sed -n 's/.*"'"$key"'"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' +} + +# Check for local installer package files +LOCAL_MODE=false +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOCAL_MANIFEST="$SCRIPT_DIR/packages/manifests/$platform.json" + +version="" +url="" +sha512="" +manifest_json="" + +if [ -f "$LOCAL_MANIFEST" ]; then + manifest_json=$(cat "$LOCAL_MANIFEST" 2>/dev/null || true) + if [ -n "$manifest_json" ]; then + version=$(parse_json_key "$manifest_json" "version") + url=$(parse_json_key "$manifest_json" "url") + sha512=$(parse_json_key "$manifest_json" "sha512") + if [ -n "$url" ] && [ -n "$sha512" ]; then + archive_filename=$(basename "$url") + LOCAL_ARCHIVE="$SCRIPT_DIR/packages/binaries/$archive_filename" + if [ -f "$LOCAL_ARCHIVE" ]; then + LOCAL_MODE=true + echo "✓ Local package files found for platform $platform. Installing offline..." + fi + fi + fi +fi + +if [ "$LOCAL_MODE" = false ]; then + # 4. Manifest Query & JSON Parsing (POSIX-Compliant) + echo "⠋ Querying release repository..." + + # Construct Platform JSON Manifest URL + MANIFEST_URL="$DOWNLOAD_BASE_URL/manifests/$platform.json" + + DOWNLOADER="" + if command -v curl >/dev/null 2>&1; then + DOWNLOADER="curl" + elif command -v wget >/dev/null 2>&1; then + DOWNLOADER="wget" + else + echo "Fatal: Either curl or wget is required but neither is installed." >&2 + exit 1 + fi + + fetch_manifest() { + if [ "$DOWNLOADER" = "curl" ]; then + curl -fsSL "$1" + else + wget -q -O - "$1" + fi + } + + manifest_json=$(fetch_manifest "$MANIFEST_URL" 2>/dev/null || true) + if [ -z "$manifest_json" ]; then + echo "Fatal: Could not connect to the release server to download the manifest. Please check your internet connection or firewall settings." >&2 + exit 1 + fi + + version=$(parse_json_key "$manifest_json" "version") + url=$(parse_json_key "$manifest_json" "url") + sha512=$(parse_json_key "$manifest_json" "sha512") + + if [ -z "$url" ] || [ -z "$sha512" ]; then + echo "Fatal: Failed to parse release manifest. The manifest may be corrupted or malformed." >&2 + exit 1 + fi +fi + +echo "✓ Latest available version: $version" + +# 5. Download & SHA512 Checksum Verification +STAGING_DIR="$HOME/.cache/antigravity/staging" +if ! mkdir -p "$STAGING_DIR" 2>/dev/null; then + echo "Error: Failed to create staging directory at $STAGING_DIR. Please check your home directory write permissions." >&2 + exit 1 +fi + +is_tar_gz=false +case "$url" in + *.tar.gz*) is_tar_gz=true ;; +esac + +if [ "$is_tar_gz" = true ]; then + staging_payload="$STAGING_DIR/agy.tar.gz" + extracted_binary="$STAGING_DIR/antigravity" +else + staging_payload="$STAGING_DIR/agy" + extracted_binary="$staging_payload" +fi + +# Robust cleanup trap to ensure staging files are reaped on any exit (success or failure) +cleanup() { + rm -f "${staging_payload:-}" "${extracted_binary:-}" 2>/dev/null || true +} +trap cleanup EXIT + +download_file() { + local src="$1" + local dst="$2" + if [ "$DOWNLOADER" = "curl" ]; then + curl -fsSL -o "$dst" "$src" + else + wget -q -O "$dst" "$src" + fi +} + +if [ "$LOCAL_MODE" = true ]; then + echo "⠋ Staging local release package..." + cp "$LOCAL_ARCHIVE" "$staging_payload" +else + echo "⠋ Downloading release package..." + if ! download_file "$url" "$staging_payload"; then + echo "Fatal: Failed to download release package from $url. Please check your internet connection or firewall settings." >&2 + exit 1 + fi +fi + +# Compute OS-Specific SHA512 Checksum +actual_hash="" +if [ "$os" = "darwin" ]; then + actual_hash=$(shasum -a 512 "$staging_payload" | cut -d' ' -f1 || true) +else + actual_hash=$(sha512sum "$staging_payload" | cut -d' ' -f1 || true) +fi + +if [ "$actual_hash" != "$sha512" ]; then + echo "Security Halt: The payload checksum does not match the manifest. The file may be corrupted or compromised. Installation aborted." >&2 + exit 1 +fi +echo "✓ Release package checksum verified." + +# 6. Direct Binary Extraction & Write Permission Validation +if ! mkdir -p "$TARGET_DIR" 2>/dev/null; then + echo "Write Error: Permission denied when attempting to create $TARGET_DIR. Please re-run the installer using the '--dir' flag to specify a writable custom directory." >&2 + exit 1 +fi + +if [ "$is_tar_gz" = true ]; then + echo "⠋ Extracting binary from archive..." + if ! tar -xzf "$staging_payload" -C "$STAGING_DIR" antigravity 2>/dev/null; then + echo "Extraction Error: Failed to extract binary from archive." >&2 + exit 1 + fi +else + echo "⠋ Copying binary directly to destination..." +fi + +if ! cp "$extracted_binary" "$BINARY_PATH" 2>/dev/null; then + echo "Write Error: Permission denied when attempting to write binary to $BINARY_PATH. Please re-run the installer using the '--dir' flag to specify a writable custom directory." >&2 + exit 1 +fi + +# Ensure Executable Permission Bit (soft fallback if filesystem doesn't support chmod) +chmod +x "$BINARY_PATH" || echo "Warning: Could not set executable permission bit on $BINARY_PATH. You may need to ensure the partition supports execution." >&2 + +# Clear macOS Gatekeeper Quarantine attribute to prevent unidentified developer blocks +if [ "$os" = "darwin" ]; then + xattr -d com.apple.quarantine "$BINARY_PATH" 2>/dev/null || true +fi + +# 7. Native Setup Handoff (Go-Native Setup Trigger - POSIX /bin/sh Compliant) +echo "⠋ Configuring shell environment..." + +if [ -n "$CUSTOM_DIR" ]; then + # Run with custom directory and absorb non-fatal soft warning exits + "$BINARY_PATH" install --dir "$CUSTOM_DIR" || true +else + # Run standard configuration and absorb non-fatal soft warning exits + "$BINARY_PATH" install || true +fi + +