From 075445250f717d5be364bc563c47c891d3d868f2 Mon Sep 17 00:00:00 2001 From: fourbroad Date: Tue, 23 Jun 2026 08:06:46 +0800 Subject: [PATCH] ci: sync from octopus@0.9.0 --- README.md | 29 ++++ install | 461 ++++++++++++++++++++++++++++++++++++++++++++++++++++ install.ps1 | 201 +++++++++++++++++++++++ 3 files changed, 691 insertions(+) create mode 100644 README.md create mode 100755 install create mode 100644 install.ps1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e84b39e --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Octopus + +The AI coding agent built for the terminal. + +## Install + +**Unix (Linux/macOS):** + +```bash +curl -fsSL http://octopus.51zxtx.com:3000/fourbroad/octopus-release/raw/branch/main/install | bash +``` + +**Windows (PowerShell):** + +```powershell +irm http://octopus.51zxtx.com:3000/fourbroad/octopus-release/raw/branch/main/install.ps1 | iex +``` + +## Documentation + +Visit the [documentation](http://octopus.51zxtx.com:3000/fourbroad/octopus/wiki) for usage guides and API reference. + +## Development + +The source code and issue tracker are available at the [private development repository](http://octopus.51zxtx.com:3000/fourbroad/octopus). + +## License + +MIT License — see the source repository for details. diff --git a/install b/install new file mode 100755 index 0000000..813cc02 --- /dev/null +++ b/install @@ -0,0 +1,461 @@ +#!/usr/bin/env bash +# Public release repository: +# http://octopus.51zxtx.com:3000/fourbroad/octopus-release +set -euo pipefail +APP=octopus + +MUTED='\033[0;2m' +RED='\033[0;31m' +ORANGE='\033[38;5;214m' +NC='\033[0m' # No Color + +usage() { + cat < Install a specific version (e.g., 1.0.180) + -b, --binary Install from a local binary instead of downloading + --no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.) + +Examples: + curl -fsSL https://octopus.51zxtx.com/install | bash + curl -fsSL https://octopus.51zxtx.com/install | bash -s -- --version 1.0.180 + ./install --binary /path/to/octopus +EOF +} + +requested_version=${VERSION:-} +no_modify_path=false +binary_path="" + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -v|--version) + if [[ -n "${2:-}" ]]; then + requested_version="$2" + shift 2 + else + echo -e "${RED}Error: --version requires a version argument${NC}" + exit 1 + fi + ;; + -b|--binary) + if [[ -n "${2:-}" ]]; then + binary_path="$2" + shift 2 + else + echo -e "${RED}Error: --binary requires a path argument${NC}" + exit 1 + fi + ;; + --no-modify-path) + no_modify_path=true + shift + ;; + *) + echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2 + shift + ;; + esac +done + +INSTALL_DIR=$HOME/.octopus/bin +mkdir -p "$INSTALL_DIR" + +# If --binary is provided, skip all download/detection logic +if [ -n "$binary_path" ]; then + if [ ! -f "$binary_path" ]; then + echo -e "${RED}Error: Binary not found at ${binary_path}${NC}" + exit 1 + fi + specific_version="local" +else + raw_os=$(uname -s) + os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]') + case "$raw_os" in + Darwin*) os="darwin" ;; + Linux*) os="linux" ;; + MINGW*|MSYS*|CYGWIN*) os="windows" ;; + esac + + arch=$(uname -m) + if [[ "$arch" == "aarch64" ]]; then + arch="arm64" + fi + if [[ "$arch" == "x86_64" ]]; then + arch="x64" + fi + + if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then + rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0) + if [ "$rosetta_flag" = "1" ]; then + arch="arm64" + fi + fi + + combo="$os-$arch" + case "$combo" in + linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64) + ;; + *) + echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}" + exit 1 + ;; + esac + + archive_ext=".zip" + if [ "$os" = "linux" ]; then + archive_ext=".tar.gz" + fi + + is_musl=false + if [ "$os" = "linux" ]; then + if [ -f /etc/alpine-release ]; then + is_musl=true + fi + + if command -v ldd >/dev/null 2>&1; then + if ldd --version 2>&1 | grep -qi musl; then + is_musl=true + fi + fi + fi + + needs_baseline=false + if [ "$arch" = "x64" ]; then + if [ "$os" = "linux" ]; then + if ! grep -qwi avx2 /proc/cpuinfo 2>/dev/null; then + needs_baseline=true + fi + fi + + if [ "$os" = "darwin" ]; then + avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0) + if [ "$avx2" != "1" ]; then + needs_baseline=true + fi + fi + + if [ "$os" = "windows" ]; then + ps="(Add-Type -MemberDefinition \"[DllImport(\"\"kernel32.dll\"\")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);\" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)" + out="" + if command -v powershell.exe >/dev/null 2>&1; then + out=$(powershell.exe -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true) + elif command -v pwsh >/dev/null 2>&1; then + out=$(pwsh -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true) + fi + out=$(echo "$out" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]') + if [ "$out" != "true" ] && [ "$out" != "1" ]; then + needs_baseline=true + fi + fi + fi + + target="$os-$arch" + if [ "$needs_baseline" = "true" ]; then + target="$target-baseline" + fi + if [ "$is_musl" = "true" ]; then + target="$target-musl" + fi + + filename="$APP-$target$archive_ext" + + + if [ "$os" = "linux" ]; then + if ! command -v tar >/dev/null 2>&1; then + echo -e "${RED}Error: 'tar' is required but not installed.${NC}" + exit 1 + fi + else + if ! command -v unzip >/dev/null 2>&1; then + echo -e "${RED}Error: 'unzip' is required but not installed.${NC}" + exit 1 + fi + fi + + if [ -z "$requested_version" ]; then + specific_version=$(curl -s "${GITEA_API_URL:-http://octopus.51zxtx.com:3000/api/v1}/repos/fourbroad/octopus-release/releases?limit=1" | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p') + + if [[ $? -ne 0 || -z "$specific_version" ]]; then + echo -e "${RED}Failed to fetch version information${NC}" + exit 1 + fi + url="${OCTOPUS_DOWNLOAD_BASE:-http://octopus.51zxtx.com:3000}/fourbroad/octopus-release/releases/download/v${specific_version}/$filename" + else + # Strip leading 'v' if present + requested_version="${requested_version#v}" + url="${OCTOPUS_DOWNLOAD_BASE:-http://octopus.51zxtx.com:3000}/fourbroad/octopus-release/releases/download/v${requested_version}/$filename" + specific_version=$requested_version + + # Verify the release exists before downloading + http_status=$(curl -sI -o /dev/null -w "%{http_code}" "${GITEA_URL:-http://octopus.51zxtx.com:3000}/fourbroad/octopus-release/releases/tag/v${requested_version}") + if [ "$http_status" = "404" ]; then + echo -e "${RED}Error: Release v${requested_version} not found${NC}" + echo -e "${MUTED}Available releases: ${GITEA_URL:-http://octopus.51zxtx.com:3000}/fourbroad/octopus-release/releases${NC}" + exit 1 + fi + fi +fi + +print_message() { + local level=$1 + local message=$2 + local color="" + + case $level in + info) color="${NC}" ;; + warning) color="${NC}" ;; + error) color="${RED}" ;; + esac + + echo -e "${color}${message}${NC}" +} + +check_version() { + if command -v octopus >/dev/null 2>&1; then + octopus_path=$(which octopus) + + ## Check the installed version + installed_version=$(octopus --version 2>/dev/null || echo "") + + if [[ "$installed_version" != "$specific_version" ]]; then + print_message info "${MUTED}Installed version: ${NC}$installed_version." + else + print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed" + exit 0 + fi + fi +} + +unbuffered_sed() { + if echo | sed -u -e "" >/dev/null 2>&1; then + sed -nu "$@" + elif echo | sed -l -e "" >/dev/null 2>&1; then + sed -nl "$@" + else + local pad="$(printf "\n%512s" "")" + sed -ne "s/$/\\${pad}/" "$@" + fi +} + +print_progress() { + local bytes="$1" + local length="$2" + [ "$length" -gt 0 ] || return 0 + + local width=50 + local percent=$(( bytes * 100 / length )) + [ "$percent" -gt 100 ] && percent=100 + local on=$(( percent * width / 100 )) + local off=$(( width - on )) + + local filled=$(printf "%*s" "$on" "") + filled=${filled// /■} + local empty=$(printf "%*s" "$off" "") + empty=${empty// /・} + + printf "\r${ORANGE}%s%s %3d%%${NC}" "$filled" "$empty" "$percent" >&4 +} + +download_with_progress() { + local url="$1" + local output="$2" + + if [ -t 2 ]; then + exec 4>&2 + else + exec 4>/dev/null + fi + + local tmp_dir=${TMPDIR:-/tmp} + local basename="${tmp_dir}/octopus_install_$$" + local tracefile="${basename}.trace" + + rm -f "$tracefile" + mkfifo "$tracefile" + + # Hide cursor + printf "\033[?25l" >&4 + + trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN + + ( + curl --trace-ascii "$tracefile" -s -L -o "$output" "$url" + ) & + local curl_pid=$! + + unbuffered_sed \ + -e 'y/ACDEGHLNORTV/acdeghlnortv/' \ + -e '/^0000: content-length:/p' \ + -e '/^<= recv data/p' \ + "$tracefile" | \ + { + local length=0 + local bytes=0 + + while IFS=" " read -r -a line; do + [ "${#line[@]}" -lt 2 ] && continue + local tag="${line[0]} ${line[1]}" + + if [ "$tag" = "0000: content-length:" ]; then + length="${line[2]}" + length=$(echo "$length" | tr -d '\r') + bytes=0 + elif [ "$tag" = "<= recv" ]; then + local size="${line[3]}" + bytes=$(( bytes + size )) + if [ "$length" -gt 0 ]; then + print_progress "$bytes" "$length" + fi + fi + done + } + + wait $curl_pid + local ret=$? + echo "" >&4 + return $ret +} + +download_and_install() { + print_message info "\n${MUTED}Installing ${NC}octopus ${MUTED}version: ${NC}$specific_version" + local tmp_dir="${TMPDIR:-/tmp}/octopus_install_$$" + mkdir -p "$tmp_dir" + + if [[ "$os" == "windows" ]] || ! [ -t 2 ] || ! download_with_progress "$url" "$tmp_dir/$filename"; then + # Fallback to standard curl on Windows, non-TTY environments, or if custom progress fails + curl -# -L -o "$tmp_dir/$filename" "$url" + fi + + if [ "$os" = "linux" ]; then + tar -xzf "$tmp_dir/$filename" -C "$tmp_dir" + else + unzip -q "$tmp_dir/$filename" -d "$tmp_dir" + fi + + mv "$tmp_dir/octopus" "$INSTALL_DIR" + chmod 755 "${INSTALL_DIR}/octopus" + rm -rf "$tmp_dir" +} + +install_from_binary() { + print_message info "\n${MUTED}Installing ${NC}octopus ${MUTED}from: ${NC}$binary_path" + cp "$binary_path" "${INSTALL_DIR}/octopus" + chmod 755 "${INSTALL_DIR}/octopus" +} + +if [ -n "$binary_path" ]; then + install_from_binary +else + check_version + download_and_install +fi + + +add_to_path() { + local config_file=$1 + local command=$2 + + if grep -Fxq "$command" "$config_file"; then + print_message info "Command already exists in $config_file, skipping write." + elif [[ -w $config_file ]]; then + echo -e "\n# octopus" >> "$config_file" + echo "$command" >> "$config_file" + print_message info "${MUTED}Successfully added ${NC}octopus ${MUTED}to \$PATH in ${NC}$config_file" + else + print_message warning "Manually add the directory to $config_file (or similar):" + print_message info " $command" + fi +} + +XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config} + +current_shell=$(basename "$SHELL") +case $current_shell in + fish) + config_files="$HOME/.config/fish/config.fish" + ;; + zsh) + config_files="${ZDOTDIR:-$HOME}/.zshrc ${ZDOTDIR:-$HOME}/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv" + ;; + bash) + config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile" + ;; + ash) + config_files="$HOME/.ashrc $HOME/.profile /etc/profile" + ;; + sh) + config_files="$HOME/.ashrc $HOME/.profile /etc/profile" + ;; + *) + # Default case if none of the above matches + config_files="$HOME/.bashrc $HOME/.bash_profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile" + ;; +esac + +if [[ "$no_modify_path" != "true" ]]; then + config_file="" + for file in $config_files; do + if [[ -f $file ]]; then + config_file=$file + break + fi + done + + if [[ -z $config_file ]]; then + print_message warning "No config file found for $current_shell. You may need to manually add to PATH:" + print_message info " export PATH=$INSTALL_DIR:\$PATH" + elif [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then + case $current_shell in + fish) + add_to_path "$config_file" "fish_add_path $INSTALL_DIR" + ;; + zsh) + add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" + ;; + bash) + add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" + ;; + ash) + add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" + ;; + sh) + add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" + ;; + *) + export PATH=$INSTALL_DIR:$PATH + print_message warning "Manually add the directory to $config_file (or similar):" + print_message info " export PATH=$INSTALL_DIR:\$PATH" + ;; + esac + fi +fi + +if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then + echo "$INSTALL_DIR" >> $GITHUB_PATH + print_message info "Added $INSTALL_DIR to \$GITHUB_PATH" +fi + +echo -e "" +echo -e "\033[38;5;214m█▀▀█ █▀▀▀ ▀▀█▀▀ █▀▀█ █▀▀█ █ █ █▀▀▀${NC}" +echo -e "\033[38;5;172m█ █ █ █ █ █ █▀▀▀ █ █ ▀▀▀█${NC}" +echo -e "\033[38;5;130m▀▀▀▀ ▀▀▀▀ ▀ ▀▀▀▀ ▀ ▀▀▀▀ ▀▀▀▀${NC}" +echo -e "" +echo -e "" +echo -e "${MUTED}Octopus includes free models, to start:${NC}" +echo -e "" +echo -e "cd ${MUTED}# Open directory${NC}" +echo -e "octopus ${MUTED}# Run command${NC}" +echo -e "" +echo -e "${MUTED}For more information visit ${NC}https://octopus.51zxtx.com/docs" +echo -e "" +echo -e "" diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..620f205 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,201 @@ +#Requires -Version 5.1 +# Public release repository: http://octopus.51zxtx.com:3000/fourbroad/octopus-release +<# +.SYNOPSIS + Octopus installer for Windows. + +.DESCRIPTION + Downloads and installs the Octopus CLI from Gitea Releases. + +.PARAMETER Version + Install a specific version (e.g. 1.0.180). Defaults to latest. + +.PARAMETER NoModifyPath + Do not add the install directory to the user PATH. + +.EXAMPLE + irm http://octopus.51zxtx.com:3000/fourbroad/octopus-release/raw/branch/dev/install.ps1 | iex + +.EXAMPLE + irm http://octopus.51zxtx.com:3000/fourbroad/octopus-release/raw/branch/dev/install.ps1 | iex; Install-Octopus -Version 0.1.3 +#> +[CmdletBinding()] +param( + [string]$Version = "", + [switch]$NoModifyPath +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +$APP = "octopus" +$REPO = "fourbroad/octopus-release" +$baseUrl = if ($env:OCTOPUS_DOWNLOAD_BASE) { $env:OCTOPUS_DOWNLOAD_BASE } else { "http://octopus.51zxtx.com:3000" } +$INSTALL_DIR = Join-Path $env:USERPROFILE ".octopus\bin" + +function Write-Color([string]$Text, [string]$Color = "White") { + Write-Host $Text -ForegroundColor $Color +} + +function Get-Arch { + $arch = $env:PROCESSOR_ARCHITECTURE + if ($arch -eq "AMD64") { return "x64" } + if ($arch -eq "ARM64") { return "arm64" } + throw "Unsupported architecture: $arch" +} + +function Test-Avx2Support { + try { + Add-Type -MemberDefinition @" + [System.Runtime.InteropServices.DllImport("kernel32.dll")] + public static extern bool IsProcessorFeaturePresent(int ProcessorFeature); +"@ -Name "Kernel32" -Namespace "Win32" -ErrorAction SilentlyContinue | Out-Null + # PF_AVX2_INSTRUCTIONS_AVAILABLE = 40 + return [Win32.Kernel32]::IsProcessorFeaturePresent(40) + } catch { + return $false + } +} + +function Get-LatestVersion { + # Resolve the latest version by following the /releases/latest redirect to + # /releases/tag/vX.Y.Z. This avoids the API, which is + # rate-limited to 60 requests/hour per IP. + $latestUrl = "$baseUrl/$REPO/releases/latest" + $location = $null + $request = [System.Net.HttpWebRequest]::Create($latestUrl) + $request.Method = "HEAD" + $request.AllowAutoRedirect = $false + $request.UserAgent = "octopus-installer" + try { + $response = $request.GetResponse() + $location = $response.Headers["Location"] + $response.Close() + } catch [System.Net.WebException] { + $resp = $_.Exception.Response + if ($null -ne $resp) { + $location = $resp.Headers["Location"] + $resp.Close() + } + } + if (-not $location) { return "" } + if ($location -match '/tag/v?([^/]+)/?$') { + return $Matches[1] -replace '^v', '' + } + return "" +} + +function Install-Octopus { + $arch = Get-Arch + + # Resolve version + if ($Version -ne "") { + $specificVersion = $Version -replace '^v', '' + # Verify release exists + $releaseUrl = "$baseUrl/$REPO/releases/tag/v$specificVersion" + try { + $null = Invoke-WebRequest -Uri $releaseUrl -Method Head -UseBasicParsing -ErrorAction Stop + } catch { + Write-Color "Error: Release v$specificVersion not found." "Red" + Write-Color "Available releases: $baseUrl/$REPO/releases" "DarkGray" + exit 1 + } + } else { + Write-Color "Fetching latest version..." "DarkGray" + $specificVersion = Get-LatestVersion + if (-not $specificVersion) { + Write-Color "Error: Failed to fetch latest version." "Red" + exit 1 + } + } + + # Check if already installed at this version + $existing = Join-Path $INSTALL_DIR "$APP.exe" + if (Test-Path $existing) { + try { + $installedVersion = & $existing --version 2>$null + if ($installedVersion -eq $specificVersion) { + Write-Color "Version $specificVersion already installed." "DarkGray" + exit 0 + } else { + Write-Color "Installed version: $installedVersion" "DarkGray" + } + } catch { } + } + + # Build filename + $target = "$APP-windows-$arch" + if ($arch -eq "x64" -and -not (Test-Avx2Support)) { + $target = "$APP-windows-$arch-baseline" + } + $filename = "$target.zip" + $downloadUrl = "$baseUrl/$REPO/releases/download/v$specificVersion/$filename" + + Write-Color "" + Write-Color "Installing octopus version: $specificVersion" "DarkGray" + Write-Color "Downloading $filename ..." "DarkGray" + + # Download + $tmpDir = Join-Path $env:TEMP "octopus_install_$PID" + New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null + $zipPath = Join-Path $tmpDir $filename + + try { + $ProgressPreference = "SilentlyContinue" + Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing + } catch { + Write-Color "Error: Failed to download $downloadUrl" "Red" + Write-Color $_.Exception.Message "Red" + Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue + exit 1 + } + + # Extract + Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force + + # Install + New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null + $exeSrc = Join-Path $tmpDir "$APP.exe" + if (-not (Test-Path $exeSrc)) { + Write-Color "Error: $APP.exe not found in archive." "Red" + Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue + exit 1 + } + Copy-Item -Path $exeSrc -Destination (Join-Path $INSTALL_DIR "$APP.exe") -Force + Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue + + # Add to PATH + if (-not $NoModifyPath) { + $userPath = [Environment]::GetEnvironmentVariable("PATH", "User") + if ($userPath -notlike "*$INSTALL_DIR*") { + [Environment]::SetEnvironmentVariable("PATH", "$INSTALL_DIR;$userPath", "User") + Write-Color "Added $INSTALL_DIR to user PATH." "DarkGray" + Write-Color "Restart your terminal for PATH changes to take effect." "Yellow" + } + } + + # CI Actions support (GitHub/Gitea) + if ($env:GITHUB_ACTIONS -eq "true" -and $env:GITHUB_PATH) { + Add-Content -Path $env:GITHUB_PATH -Value $INSTALL_DIR + Write-Color "Added $INSTALL_DIR to GITHUB_PATH." "DarkGray" + } + + Write-Host "" + Write-Host " ___ _" -ForegroundColor DarkYellow + Write-Host " / _ \ ___| |_ ___ _ __ _ _ ___" -ForegroundColor DarkYellow + Write-Host "| | | |/ __| __/ _ \| '_ \| | | / __|" -ForegroundColor Yellow + Write-Host "| |_| | (__| || (_) | |_) | |_| \__ \" -ForegroundColor Yellow + Write-Host " \___/ \___|\__\___/| .__/ \__,_|___/" -ForegroundColor White + Write-Host " |_|" -ForegroundColor White + Write-Host "" + Write-Host "Octopus includes free models, to start:" -ForegroundColor DarkGray + Write-Host "" + Write-Host " cd " -NoNewline; Write-Host "# Open directory" -ForegroundColor DarkGray + Write-Host " octopus " -NoNewline; Write-Host "# Run command" -ForegroundColor DarkGray + Write-Host "" + Write-Host "For more information visit " -NoNewline -ForegroundColor DarkGray + Write-Host "https://octopus.51zxtx.com/docs" + Write-Host "" +} + +Install-Octopus