ci: sync from octopus@0.25.0 (readme link fix)

This commit is contained in:
Release Sync Bot
2026-10-06 20:46:13 +08:00
parent 029031ea75
commit 74ab30a4f3
3 changed files with 743 additions and 743 deletions
+461 -461
View File
@@ -1,461 +1,461 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Public release repository: # Public release repository:
# https://eightarms.net/Octopus/octopus-release # https://eightarms.net/Octopus/octopus-release
set -euo pipefail set -euo pipefail
APP=octopus APP=octopus
MUTED='\033[0;2m' MUTED='\033[0;2m'
RED='\033[0;31m' RED='\033[0;31m'
ORANGE='\033[38;5;214m' ORANGE='\033[38;5;214m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
usage() { usage() {
cat <<EOF cat <<EOF
Octopus Installer Octopus Installer
Usage: install.sh [options] Usage: install.sh [options]
Options: Options:
-h, --help Display this help message -h, --help Display this help message
-v, --version <version> Install a specific version (e.g., 1.0.180) -v, --version <version> Install a specific version (e.g., 1.0.180)
-b, --binary <path> Install from a local binary instead of downloading -b, --binary <path> Install from a local binary instead of downloading
--no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.) --no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
Examples: Examples:
curl -fsSL https://eightarms.net/install | bash curl -fsSL https://eightarms.net/install | bash
curl -fsSL https://eightarms.net/install | bash -s -- --version 1.0.180 curl -fsSL https://eightarms.net/install | bash -s -- --version 1.0.180
./install --binary /path/to/octopus ./install --binary /path/to/octopus
EOF EOF
} }
requested_version=${VERSION:-} requested_version=${VERSION:-}
no_modify_path=false no_modify_path=false
binary_path="" binary_path=""
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
-h|--help) -h|--help)
usage usage
exit 0 exit 0
;; ;;
-v|--version) -v|--version)
if [[ -n "${2:-}" ]]; then if [[ -n "${2:-}" ]]; then
requested_version="$2" requested_version="$2"
shift 2 shift 2
else else
echo -e "${RED}Error: --version requires a version argument${NC}" echo -e "${RED}Error: --version requires a version argument${NC}"
exit 1 exit 1
fi fi
;; ;;
-b|--binary) -b|--binary)
if [[ -n "${2:-}" ]]; then if [[ -n "${2:-}" ]]; then
binary_path="$2" binary_path="$2"
shift 2 shift 2
else else
echo -e "${RED}Error: --binary requires a path argument${NC}" echo -e "${RED}Error: --binary requires a path argument${NC}"
exit 1 exit 1
fi fi
;; ;;
--no-modify-path) --no-modify-path)
no_modify_path=true no_modify_path=true
shift shift
;; ;;
*) *)
echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2 echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2
shift shift
;; ;;
esac esac
done done
INSTALL_DIR=$HOME/.octopus/bin INSTALL_DIR=$HOME/.octopus/bin
mkdir -p "$INSTALL_DIR" mkdir -p "$INSTALL_DIR"
# If --binary is provided, skip all download/detection logic # If --binary is provided, skip all download/detection logic
if [ -n "$binary_path" ]; then if [ -n "$binary_path" ]; then
if [ ! -f "$binary_path" ]; then if [ ! -f "$binary_path" ]; then
echo -e "${RED}Error: Binary not found at ${binary_path}${NC}" echo -e "${RED}Error: Binary not found at ${binary_path}${NC}"
exit 1 exit 1
fi fi
specific_version="local" specific_version="local"
else else
raw_os=$(uname -s) raw_os=$(uname -s)
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]') os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
case "$raw_os" in case "$raw_os" in
Darwin*) os="darwin" ;; Darwin*) os="darwin" ;;
Linux*) os="linux" ;; Linux*) os="linux" ;;
MINGW*|MSYS*|CYGWIN*) os="windows" ;; MINGW*|MSYS*|CYGWIN*) os="windows" ;;
esac esac
arch=$(uname -m) arch=$(uname -m)
if [[ "$arch" == "aarch64" ]]; then if [[ "$arch" == "aarch64" ]]; then
arch="arm64" arch="arm64"
fi fi
if [[ "$arch" == "x86_64" ]]; then if [[ "$arch" == "x86_64" ]]; then
arch="x64" arch="x64"
fi fi
if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then
rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0) rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
if [ "$rosetta_flag" = "1" ]; then if [ "$rosetta_flag" = "1" ]; then
arch="arm64" arch="arm64"
fi fi
fi fi
combo="$os-$arch" combo="$os-$arch"
case "$combo" in case "$combo" in
linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64) linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64)
;; ;;
*) *)
echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}" echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
exit 1 exit 1
;; ;;
esac esac
archive_ext=".zip" archive_ext=".zip"
if [ "$os" = "linux" ]; then if [ "$os" = "linux" ]; then
archive_ext=".tar.gz" archive_ext=".tar.gz"
fi fi
is_musl=false is_musl=false
if [ "$os" = "linux" ]; then if [ "$os" = "linux" ]; then
if [ -f /etc/alpine-release ]; then if [ -f /etc/alpine-release ]; then
is_musl=true is_musl=true
fi fi
if command -v ldd >/dev/null 2>&1; then if command -v ldd >/dev/null 2>&1; then
if ldd --version 2>&1 | grep -qi musl; then if ldd --version 2>&1 | grep -qi musl; then
is_musl=true is_musl=true
fi fi
fi fi
fi fi
needs_baseline=false needs_baseline=false
if [ "$arch" = "x64" ]; then if [ "$arch" = "x64" ]; then
if [ "$os" = "linux" ]; then if [ "$os" = "linux" ]; then
if ! grep -qwi avx2 /proc/cpuinfo 2>/dev/null; then if ! grep -qwi avx2 /proc/cpuinfo 2>/dev/null; then
needs_baseline=true needs_baseline=true
fi fi
fi fi
if [ "$os" = "darwin" ]; then if [ "$os" = "darwin" ]; then
avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0) avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0)
if [ "$avx2" != "1" ]; then if [ "$avx2" != "1" ]; then
needs_baseline=true needs_baseline=true
fi fi
fi fi
if [ "$os" = "windows" ]; then 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)" ps="(Add-Type -MemberDefinition \"[DllImport(\"\"kernel32.dll\"\")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);\" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)"
out="" out=""
if command -v powershell.exe >/dev/null 2>&1; then if command -v powershell.exe >/dev/null 2>&1; then
out=$(powershell.exe -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true) out=$(powershell.exe -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true)
elif command -v pwsh >/dev/null 2>&1; then elif command -v pwsh >/dev/null 2>&1; then
out=$(pwsh -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true) out=$(pwsh -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true)
fi fi
out=$(echo "$out" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]') out=$(echo "$out" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')
if [ "$out" != "true" ] && [ "$out" != "1" ]; then if [ "$out" != "true" ] && [ "$out" != "1" ]; then
needs_baseline=true needs_baseline=true
fi fi
fi fi
fi fi
target="$os-$arch" target="$os-$arch"
if [ "$needs_baseline" = "true" ]; then if [ "$needs_baseline" = "true" ]; then
target="$target-baseline" target="$target-baseline"
fi fi
if [ "$is_musl" = "true" ]; then if [ "$is_musl" = "true" ]; then
target="$target-musl" target="$target-musl"
fi fi
filename="$APP-$target$archive_ext" filename="$APP-$target$archive_ext"
if [ "$os" = "linux" ]; then if [ "$os" = "linux" ]; then
if ! command -v tar >/dev/null 2>&1; then if ! command -v tar >/dev/null 2>&1; then
echo -e "${RED}Error: 'tar' is required but not installed.${NC}" echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
exit 1 exit 1
fi fi
else else
if ! command -v unzip >/dev/null 2>&1; then if ! command -v unzip >/dev/null 2>&1; then
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}" echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
exit 1 exit 1
fi fi
fi fi
if [ -z "$requested_version" ]; then if [ -z "$requested_version" ]; then
specific_version=$(curl -s "${GITEA_API_URL:-https://eightarms.net/api/v1}/repos/Octopus/octopus-release/releases?limit=1" | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p') specific_version=$(curl -s "${GITEA_API_URL:-https://eightarms.net/api/v1}/repos/Octopus/octopus-release/releases?limit=1" | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
if [[ $? -ne 0 || -z "$specific_version" ]]; then if [[ $? -ne 0 || -z "$specific_version" ]]; then
echo -e "${RED}Failed to fetch version information${NC}" echo -e "${RED}Failed to fetch version information${NC}"
exit 1 exit 1
fi fi
url="${OCTOPUS_DOWNLOAD_BASE:-https://eightarms.net}/Octopus/octopus-release/releases/download/v${specific_version}/$filename" url="${OCTOPUS_DOWNLOAD_BASE:-https://eightarms.net}/Octopus/octopus-release/releases/download/v${specific_version}/$filename"
else else
# Strip leading 'v' if present # Strip leading 'v' if present
requested_version="${requested_version#v}" requested_version="${requested_version#v}"
url="${OCTOPUS_DOWNLOAD_BASE:-https://eightarms.net}/Octopus/octopus-release/releases/download/v${requested_version}/$filename" url="${OCTOPUS_DOWNLOAD_BASE:-https://eightarms.net}/Octopus/octopus-release/releases/download/v${requested_version}/$filename"
specific_version=$requested_version specific_version=$requested_version
# Verify the release exists before downloading # Verify the release exists before downloading
http_status=$(curl -sI -o /dev/null -w "%{http_code}" "${GITEA_URL:-https://eightarms.net}/Octopus/octopus-release/releases/tag/v${requested_version}") http_status=$(curl -sI -o /dev/null -w "%{http_code}" "${GITEA_URL:-https://eightarms.net}/Octopus/octopus-release/releases/tag/v${requested_version}")
if [ "$http_status" = "404" ]; then if [ "$http_status" = "404" ]; then
echo -e "${RED}Error: Release v${requested_version} not found${NC}" echo -e "${RED}Error: Release v${requested_version} not found${NC}"
echo -e "${MUTED}Available releases: ${GITEA_URL:-https://eightarms.net}/Octopus/octopus-release/releases${NC}" echo -e "${MUTED}Available releases: ${GITEA_URL:-https://eightarms.net}/Octopus/octopus-release/releases${NC}"
exit 1 exit 1
fi fi
fi fi
fi fi
print_message() { print_message() {
local level=$1 local level=$1
local message=$2 local message=$2
local color="" local color=""
case $level in case $level in
info) color="${NC}" ;; info) color="${NC}" ;;
warning) color="${NC}" ;; warning) color="${NC}" ;;
error) color="${RED}" ;; error) color="${RED}" ;;
esac esac
echo -e "${color}${message}${NC}" echo -e "${color}${message}${NC}"
} }
check_version() { check_version() {
if command -v octopus >/dev/null 2>&1; then if command -v octopus >/dev/null 2>&1; then
octopus_path=$(which octopus) octopus_path=$(which octopus)
## Check the installed version ## Check the installed version
installed_version=$(octopus --version 2>/dev/null || echo "") installed_version=$(octopus --version 2>/dev/null || echo "")
if [[ "$installed_version" != "$specific_version" ]]; then if [[ "$installed_version" != "$specific_version" ]]; then
print_message info "${MUTED}Installed version: ${NC}$installed_version." print_message info "${MUTED}Installed version: ${NC}$installed_version."
else else
print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed" print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed"
exit 0 exit 0
fi fi
fi fi
} }
unbuffered_sed() { unbuffered_sed() {
if echo | sed -u -e "" >/dev/null 2>&1; then if echo | sed -u -e "" >/dev/null 2>&1; then
sed -nu "$@" sed -nu "$@"
elif echo | sed -l -e "" >/dev/null 2>&1; then elif echo | sed -l -e "" >/dev/null 2>&1; then
sed -nl "$@" sed -nl "$@"
else else
local pad="$(printf "\n%512s" "")" local pad="$(printf "\n%512s" "")"
sed -ne "s/$/\\${pad}/" "$@" sed -ne "s/$/\\${pad}/" "$@"
fi fi
} }
print_progress() { print_progress() {
local bytes="$1" local bytes="$1"
local length="$2" local length="$2"
[ "$length" -gt 0 ] || return 0 [ "$length" -gt 0 ] || return 0
local width=50 local width=50
local percent=$(( bytes * 100 / length )) local percent=$(( bytes * 100 / length ))
[ "$percent" -gt 100 ] && percent=100 [ "$percent" -gt 100 ] && percent=100
local on=$(( percent * width / 100 )) local on=$(( percent * width / 100 ))
local off=$(( width - on )) local off=$(( width - on ))
local filled=$(printf "%*s" "$on" "") local filled=$(printf "%*s" "$on" "")
filled=${filled// /■} filled=${filled// /■}
local empty=$(printf "%*s" "$off" "") local empty=$(printf "%*s" "$off" "")
empty=${empty// /・} empty=${empty// /・}
printf "\r${ORANGE}%s%s %3d%%${NC}" "$filled" "$empty" "$percent" >&4 printf "\r${ORANGE}%s%s %3d%%${NC}" "$filled" "$empty" "$percent" >&4
} }
download_with_progress() { download_with_progress() {
local url="$1" local url="$1"
local output="$2" local output="$2"
if [ -t 2 ]; then if [ -t 2 ]; then
exec 4>&2 exec 4>&2
else else
exec 4>/dev/null exec 4>/dev/null
fi fi
local tmp_dir=${TMPDIR:-/tmp} local tmp_dir=${TMPDIR:-/tmp}
local basename="${tmp_dir}/octopus_install_$$" local basename="${tmp_dir}/octopus_install_$$"
local tracefile="${basename}.trace" local tracefile="${basename}.trace"
rm -f "$tracefile" rm -f "$tracefile"
mkfifo "$tracefile" mkfifo "$tracefile"
# Hide cursor # Hide cursor
printf "\033[?25l" >&4 printf "\033[?25l" >&4
trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN
( (
curl --trace-ascii "$tracefile" -s -L -o "$output" "$url" curl --trace-ascii "$tracefile" -s -L -o "$output" "$url"
) & ) &
local curl_pid=$! local curl_pid=$!
unbuffered_sed \ unbuffered_sed \
-e 'y/ACDEGHLNORTV/acdeghlnortv/' \ -e 'y/ACDEGHLNORTV/acdeghlnortv/' \
-e '/^0000: content-length:/p' \ -e '/^0000: content-length:/p' \
-e '/^<= recv data/p' \ -e '/^<= recv data/p' \
"$tracefile" | \ "$tracefile" | \
{ {
local length=0 local length=0
local bytes=0 local bytes=0
while IFS=" " read -r -a line; do while IFS=" " read -r -a line; do
[ "${#line[@]}" -lt 2 ] && continue [ "${#line[@]}" -lt 2 ] && continue
local tag="${line[0]} ${line[1]}" local tag="${line[0]} ${line[1]}"
if [ "$tag" = "0000: content-length:" ]; then if [ "$tag" = "0000: content-length:" ]; then
length="${line[2]}" length="${line[2]}"
length=$(echo "$length" | tr -d '\r') length=$(echo "$length" | tr -d '\r')
bytes=0 bytes=0
elif [ "$tag" = "<= recv" ]; then elif [ "$tag" = "<= recv" ]; then
local size="${line[3]}" local size="${line[3]}"
bytes=$(( bytes + size )) bytes=$(( bytes + size ))
if [ "$length" -gt 0 ]; then if [ "$length" -gt 0 ]; then
print_progress "$bytes" "$length" print_progress "$bytes" "$length"
fi fi
fi fi
done done
} }
wait $curl_pid wait $curl_pid
local ret=$? local ret=$?
echo "" >&4 echo "" >&4
return $ret return $ret
} }
download_and_install() { download_and_install() {
print_message info "\n${MUTED}Installing ${NC}octopus ${MUTED}version: ${NC}$specific_version" print_message info "\n${MUTED}Installing ${NC}octopus ${MUTED}version: ${NC}$specific_version"
local tmp_dir="${TMPDIR:-/tmp}/octopus_install_$$" local tmp_dir="${TMPDIR:-/tmp}/octopus_install_$$"
mkdir -p "$tmp_dir" mkdir -p "$tmp_dir"
if [[ "$os" == "windows" ]] || ! [ -t 2 ] || ! download_with_progress "$url" "$tmp_dir/$filename"; then 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 # Fallback to standard curl on Windows, non-TTY environments, or if custom progress fails
curl -# -L -o "$tmp_dir/$filename" "$url" curl -# -L -o "$tmp_dir/$filename" "$url"
fi fi
if [ "$os" = "linux" ]; then if [ "$os" = "linux" ]; then
tar -xzf "$tmp_dir/$filename" -C "$tmp_dir" tar -xzf "$tmp_dir/$filename" -C "$tmp_dir"
else else
unzip -q "$tmp_dir/$filename" -d "$tmp_dir" unzip -q "$tmp_dir/$filename" -d "$tmp_dir"
fi fi
mv "$tmp_dir/octopus" "$INSTALL_DIR" mv "$tmp_dir/octopus" "$INSTALL_DIR"
chmod 755 "${INSTALL_DIR}/octopus" chmod 755 "${INSTALL_DIR}/octopus"
rm -rf "$tmp_dir" rm -rf "$tmp_dir"
} }
install_from_binary() { install_from_binary() {
print_message info "\n${MUTED}Installing ${NC}octopus ${MUTED}from: ${NC}$binary_path" print_message info "\n${MUTED}Installing ${NC}octopus ${MUTED}from: ${NC}$binary_path"
cp "$binary_path" "${INSTALL_DIR}/octopus" cp "$binary_path" "${INSTALL_DIR}/octopus"
chmod 755 "${INSTALL_DIR}/octopus" chmod 755 "${INSTALL_DIR}/octopus"
} }
if [ -n "$binary_path" ]; then if [ -n "$binary_path" ]; then
install_from_binary install_from_binary
else else
check_version check_version
download_and_install download_and_install
fi fi
add_to_path() { add_to_path() {
local config_file=$1 local config_file=$1
local command=$2 local command=$2
if grep -Fxq "$command" "$config_file"; then if grep -Fxq "$command" "$config_file"; then
print_message info "Command already exists in $config_file, skipping write." print_message info "Command already exists in $config_file, skipping write."
elif [[ -w $config_file ]]; then elif [[ -w $config_file ]]; then
echo -e "\n# octopus" >> "$config_file" echo -e "\n# octopus" >> "$config_file"
echo "$command" >> "$config_file" echo "$command" >> "$config_file"
print_message info "${MUTED}Successfully added ${NC}octopus ${MUTED}to \$PATH in ${NC}$config_file" print_message info "${MUTED}Successfully added ${NC}octopus ${MUTED}to \$PATH in ${NC}$config_file"
else else
print_message warning "Manually add the directory to $config_file (or similar):" print_message warning "Manually add the directory to $config_file (or similar):"
print_message info " $command" print_message info " $command"
fi fi
} }
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config} XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
current_shell=$(basename "$SHELL") current_shell=$(basename "$SHELL")
case $current_shell in case $current_shell in
fish) fish)
config_files="$HOME/.config/fish/config.fish" config_files="$HOME/.config/fish/config.fish"
;; ;;
zsh) zsh)
config_files="${ZDOTDIR:-$HOME}/.zshrc ${ZDOTDIR:-$HOME}/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv" config_files="${ZDOTDIR:-$HOME}/.zshrc ${ZDOTDIR:-$HOME}/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv"
;; ;;
bash) bash)
config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile" config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
;; ;;
ash) ash)
config_files="$HOME/.ashrc $HOME/.profile /etc/profile" config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
;; ;;
sh) sh)
config_files="$HOME/.ashrc $HOME/.profile /etc/profile" config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
;; ;;
*) *)
# Default case if none of the above matches # 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" config_files="$HOME/.bashrc $HOME/.bash_profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
;; ;;
esac esac
if [[ "$no_modify_path" != "true" ]]; then if [[ "$no_modify_path" != "true" ]]; then
config_file="" config_file=""
for file in $config_files; do for file in $config_files; do
if [[ -f $file ]]; then if [[ -f $file ]]; then
config_file=$file config_file=$file
break break
fi fi
done done
if [[ -z $config_file ]]; then 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 warning "No config file found for $current_shell. You may need to manually add to PATH:"
print_message info " export PATH=$INSTALL_DIR:\$PATH" print_message info " export PATH=$INSTALL_DIR:\$PATH"
elif [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then elif [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
case $current_shell in case $current_shell in
fish) fish)
add_to_path "$config_file" "fish_add_path $INSTALL_DIR" add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
;; ;;
zsh) zsh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;; ;;
bash) bash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;; ;;
ash) ash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;; ;;
sh) sh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH" add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;; ;;
*) *)
export PATH=$INSTALL_DIR:$PATH export PATH=$INSTALL_DIR:$PATH
print_message warning "Manually add the directory to $config_file (or similar):" print_message warning "Manually add the directory to $config_file (or similar):"
print_message info " export PATH=$INSTALL_DIR:\$PATH" print_message info " export PATH=$INSTALL_DIR:\$PATH"
;; ;;
esac esac
fi fi
fi fi
if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
echo "$INSTALL_DIR" >> $GITHUB_PATH echo "$INSTALL_DIR" >> $GITHUB_PATH
print_message info "Added $INSTALL_DIR to \$GITHUB_PATH" print_message info "Added $INSTALL_DIR to \$GITHUB_PATH"
fi fi
echo -e "" echo -e ""
echo -e "\033[38;5;214m█▀▀█ █▀▀▀ ▀▀█▀▀ █▀▀█ █▀▀█ █ █ █▀▀▀${NC}" echo -e "\033[38;5;214m█▀▀█ █▀▀▀ ▀▀█▀▀ █▀▀█ █▀▀█ █ █ █▀▀▀${NC}"
echo -e "\033[38;5;172m█ █ █ █ █ █ █▀▀▀ █ █ ▀▀▀█${NC}" echo -e "\033[38;5;172m█ █ █ █ █ █ █▀▀▀ █ █ ▀▀▀█${NC}"
echo -e "\033[38;5;130m▀▀▀▀ ▀▀▀▀ ▀ ▀▀▀▀ ▀ ▀▀▀▀ ▀▀▀▀${NC}" echo -e "\033[38;5;130m▀▀▀▀ ▀▀▀▀ ▀ ▀▀▀▀ ▀ ▀▀▀▀ ▀▀▀▀${NC}"
echo -e "" echo -e ""
echo -e "" echo -e ""
echo -e "${MUTED}Octopus includes free models, to start:${NC}" echo -e "${MUTED}Octopus includes free models, to start:${NC}"
echo -e "" echo -e ""
echo -e "cd <project> ${MUTED}# Open directory${NC}" echo -e "cd <project> ${MUTED}# Open directory${NC}"
echo -e "octopus ${MUTED}# Run command${NC}" echo -e "octopus ${MUTED}# Run command${NC}"
echo -e "" echo -e ""
echo -e "${MUTED}For more information visit ${NC}https://eightarms.net/docs" echo -e "${MUTED}For more information visit ${NC}https://eightarms.net/docs"
echo -e "" echo -e ""
echo -e "" echo -e ""
+201 -201
View File
@@ -1,201 +1,201 @@
#Requires -Version 5.1 #Requires -Version 5.1
# Public release repository: https://eightarms.net/Octopus/octopus-release # Public release repository: https://eightarms.net/Octopus/octopus-release
<# <#
.SYNOPSIS .SYNOPSIS
Octopus installer for Windows. Octopus installer for Windows.
.DESCRIPTION .DESCRIPTION
Downloads and installs the Octopus CLI from Gitea Releases. Downloads and installs the Octopus CLI from Gitea Releases.
.PARAMETER Version .PARAMETER Version
Install a specific version (e.g. 1.0.180). Defaults to latest. Install a specific version (e.g. 1.0.180). Defaults to latest.
.PARAMETER NoModifyPath .PARAMETER NoModifyPath
Do not add the install directory to the user PATH. Do not add the install directory to the user PATH.
.EXAMPLE .EXAMPLE
irm https://eightarms.net/Octopus/octopus-release/raw/branch/dev/install.ps1 | iex irm https://eightarms.net/Octopus/octopus-release/raw/branch/dev/install.ps1 | iex
.EXAMPLE .EXAMPLE
irm https://eightarms.net/Octopus/octopus-release/raw/branch/dev/install.ps1 | iex; Install-Octopus -Version 0.1.3 irm https://eightarms.net/Octopus/octopus-release/raw/branch/dev/install.ps1 | iex; Install-Octopus -Version 0.1.3
#> #>
[CmdletBinding()] [CmdletBinding()]
param( param(
[string]$Version = "", [string]$Version = "",
[switch]$NoModifyPath [switch]$NoModifyPath
) )
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$APP = "octopus" $APP = "octopus"
$REPO = "Octopus/octopus-release" $REPO = "Octopus/octopus-release"
$baseUrl = if ($env:OCTOPUS_DOWNLOAD_BASE) { $env:OCTOPUS_DOWNLOAD_BASE } else { "https://eightarms.net" } $baseUrl = if ($env:OCTOPUS_DOWNLOAD_BASE) { $env:OCTOPUS_DOWNLOAD_BASE } else { "https://eightarms.net" }
$INSTALL_DIR = Join-Path $env:USERPROFILE ".octopus\bin" $INSTALL_DIR = Join-Path $env:USERPROFILE ".octopus\bin"
function Write-Color([string]$Text, [string]$Color = "White") { function Write-Color([string]$Text, [string]$Color = "White") {
Write-Host $Text -ForegroundColor $Color Write-Host $Text -ForegroundColor $Color
} }
function Get-Arch { function Get-Arch {
$arch = $env:PROCESSOR_ARCHITECTURE $arch = $env:PROCESSOR_ARCHITECTURE
if ($arch -eq "AMD64") { return "x64" } if ($arch -eq "AMD64") { return "x64" }
if ($arch -eq "ARM64") { return "arm64" } if ($arch -eq "ARM64") { return "arm64" }
throw "Unsupported architecture: $arch" throw "Unsupported architecture: $arch"
} }
function Test-Avx2Support { function Test-Avx2Support {
try { try {
Add-Type -MemberDefinition @" Add-Type -MemberDefinition @"
[System.Runtime.InteropServices.DllImport("kernel32.dll")] [System.Runtime.InteropServices.DllImport("kernel32.dll")]
public static extern bool IsProcessorFeaturePresent(int ProcessorFeature); public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);
"@ -Name "Kernel32" -Namespace "Win32" -ErrorAction SilentlyContinue | Out-Null "@ -Name "Kernel32" -Namespace "Win32" -ErrorAction SilentlyContinue | Out-Null
# PF_AVX2_INSTRUCTIONS_AVAILABLE = 40 # PF_AVX2_INSTRUCTIONS_AVAILABLE = 40
return [Win32.Kernel32]::IsProcessorFeaturePresent(40) return [Win32.Kernel32]::IsProcessorFeaturePresent(40)
} catch { } catch {
return $false return $false
} }
} }
function Get-LatestVersion { function Get-LatestVersion {
# Resolve the latest version by following the /releases/latest redirect to # Resolve the latest version by following the /releases/latest redirect to
# /releases/tag/vX.Y.Z. This avoids the API, which is # /releases/tag/vX.Y.Z. This avoids the API, which is
# rate-limited to 60 requests/hour per IP. # rate-limited to 60 requests/hour per IP.
$latestUrl = "$baseUrl/$REPO/releases/latest" $latestUrl = "$baseUrl/$REPO/releases/latest"
$location = $null $location = $null
$request = [System.Net.HttpWebRequest]::Create($latestUrl) $request = [System.Net.HttpWebRequest]::Create($latestUrl)
$request.Method = "HEAD" $request.Method = "HEAD"
$request.AllowAutoRedirect = $false $request.AllowAutoRedirect = $false
$request.UserAgent = "octopus-installer" $request.UserAgent = "octopus-installer"
try { try {
$response = $request.GetResponse() $response = $request.GetResponse()
$location = $response.Headers["Location"] $location = $response.Headers["Location"]
$response.Close() $response.Close()
} catch [System.Net.WebException] { } catch [System.Net.WebException] {
$resp = $_.Exception.Response $resp = $_.Exception.Response
if ($null -ne $resp) { if ($null -ne $resp) {
$location = $resp.Headers["Location"] $location = $resp.Headers["Location"]
$resp.Close() $resp.Close()
} }
} }
if (-not $location) { return "" } if (-not $location) { return "" }
if ($location -match '/tag/v?([^/]+)/?$') { if ($location -match '/tag/v?([^/]+)/?$') {
return $Matches[1] -replace '^v', '' return $Matches[1] -replace '^v', ''
} }
return "" return ""
} }
function Install-Octopus { function Install-Octopus {
$arch = Get-Arch $arch = Get-Arch
# Resolve version # Resolve version
if ($Version -ne "") { if ($Version -ne "") {
$specificVersion = $Version -replace '^v', '' $specificVersion = $Version -replace '^v', ''
# Verify release exists # Verify release exists
$releaseUrl = "$baseUrl/$REPO/releases/tag/v$specificVersion" $releaseUrl = "$baseUrl/$REPO/releases/tag/v$specificVersion"
try { try {
$null = Invoke-WebRequest -Uri $releaseUrl -Method Head -UseBasicParsing -ErrorAction Stop $null = Invoke-WebRequest -Uri $releaseUrl -Method Head -UseBasicParsing -ErrorAction Stop
} catch { } catch {
Write-Color "Error: Release v$specificVersion not found." "Red" Write-Color "Error: Release v$specificVersion not found." "Red"
Write-Color "Available releases: $baseUrl/$REPO/releases" "DarkGray" Write-Color "Available releases: $baseUrl/$REPO/releases" "DarkGray"
exit 1 exit 1
} }
} else { } else {
Write-Color "Fetching latest version..." "DarkGray" Write-Color "Fetching latest version..." "DarkGray"
$specificVersion = Get-LatestVersion $specificVersion = Get-LatestVersion
if (-not $specificVersion) { if (-not $specificVersion) {
Write-Color "Error: Failed to fetch latest version." "Red" Write-Color "Error: Failed to fetch latest version." "Red"
exit 1 exit 1
} }
} }
# Check if already installed at this version # Check if already installed at this version
$existing = Join-Path $INSTALL_DIR "$APP.exe" $existing = Join-Path $INSTALL_DIR "$APP.exe"
if (Test-Path $existing) { if (Test-Path $existing) {
try { try {
$installedVersion = & $existing --version 2>$null $installedVersion = & $existing --version 2>$null
if ($installedVersion -eq $specificVersion) { if ($installedVersion -eq $specificVersion) {
Write-Color "Version $specificVersion already installed." "DarkGray" Write-Color "Version $specificVersion already installed." "DarkGray"
exit 0 exit 0
} else { } else {
Write-Color "Installed version: $installedVersion" "DarkGray" Write-Color "Installed version: $installedVersion" "DarkGray"
} }
} catch { } } catch { }
} }
# Build filename # Build filename
$target = "$APP-windows-$arch" $target = "$APP-windows-$arch"
if ($arch -eq "x64" -and -not (Test-Avx2Support)) { if ($arch -eq "x64" -and -not (Test-Avx2Support)) {
$target = "$APP-windows-$arch-baseline" $target = "$APP-windows-$arch-baseline"
} }
$filename = "$target.zip" $filename = "$target.zip"
$downloadUrl = "$baseUrl/$REPO/releases/download/v$specificVersion/$filename" $downloadUrl = "$baseUrl/$REPO/releases/download/v$specificVersion/$filename"
Write-Color "" Write-Color ""
Write-Color "Installing octopus version: $specificVersion" "DarkGray" Write-Color "Installing octopus version: $specificVersion" "DarkGray"
Write-Color "Downloading $filename ..." "DarkGray" Write-Color "Downloading $filename ..." "DarkGray"
# Download # Download
$tmpDir = Join-Path $env:TEMP "octopus_install_$PID" $tmpDir = Join-Path $env:TEMP "octopus_install_$PID"
New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null
$zipPath = Join-Path $tmpDir $filename $zipPath = Join-Path $tmpDir $filename
try { try {
$ProgressPreference = "SilentlyContinue" $ProgressPreference = "SilentlyContinue"
Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing
} catch { } catch {
Write-Color "Error: Failed to download $downloadUrl" "Red" Write-Color "Error: Failed to download $downloadUrl" "Red"
Write-Color $_.Exception.Message "Red" Write-Color $_.Exception.Message "Red"
Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue
exit 1 exit 1
} }
# Extract # Extract
Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force
# Install # Install
New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
$exeSrc = Join-Path $tmpDir "$APP.exe" $exeSrc = Join-Path $tmpDir "$APP.exe"
if (-not (Test-Path $exeSrc)) { if (-not (Test-Path $exeSrc)) {
Write-Color "Error: $APP.exe not found in archive." "Red" Write-Color "Error: $APP.exe not found in archive." "Red"
Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue
exit 1 exit 1
} }
Copy-Item -Path $exeSrc -Destination (Join-Path $INSTALL_DIR "$APP.exe") -Force Copy-Item -Path $exeSrc -Destination (Join-Path $INSTALL_DIR "$APP.exe") -Force
Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue
# Add to PATH # Add to PATH
if (-not $NoModifyPath) { if (-not $NoModifyPath) {
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User") $userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
if ($userPath -notlike "*$INSTALL_DIR*") { if ($userPath -notlike "*$INSTALL_DIR*") {
[Environment]::SetEnvironmentVariable("PATH", "$INSTALL_DIR;$userPath", "User") [Environment]::SetEnvironmentVariable("PATH", "$INSTALL_DIR;$userPath", "User")
Write-Color "Added $INSTALL_DIR to user PATH." "DarkGray" Write-Color "Added $INSTALL_DIR to user PATH." "DarkGray"
Write-Color "Restart your terminal for PATH changes to take effect." "Yellow" Write-Color "Restart your terminal for PATH changes to take effect." "Yellow"
} }
} }
# CI Actions support (GitHub/Gitea) # CI Actions support (GitHub/Gitea)
if ($env:GITHUB_ACTIONS -eq "true" -and $env:GITHUB_PATH) { if ($env:GITHUB_ACTIONS -eq "true" -and $env:GITHUB_PATH) {
Add-Content -Path $env:GITHUB_PATH -Value $INSTALL_DIR Add-Content -Path $env:GITHUB_PATH -Value $INSTALL_DIR
Write-Color "Added $INSTALL_DIR to GITHUB_PATH." "DarkGray" Write-Color "Added $INSTALL_DIR to GITHUB_PATH." "DarkGray"
} }
Write-Host "" Write-Host ""
Write-Host " ___ _" -ForegroundColor DarkYellow Write-Host " ___ _" -ForegroundColor DarkYellow
Write-Host " / _ \ ___| |_ ___ _ __ _ _ ___" -ForegroundColor DarkYellow Write-Host " / _ \ ___| |_ ___ _ __ _ _ ___" -ForegroundColor DarkYellow
Write-Host "| | | |/ __| __/ _ \| '_ \| | | / __|" -ForegroundColor Yellow Write-Host "| | | |/ __| __/ _ \| '_ \| | | / __|" -ForegroundColor Yellow
Write-Host "| |_| | (__| || (_) | |_) | |_| \__ \" -ForegroundColor Yellow Write-Host "| |_| | (__| || (_) | |_) | |_| \__ \" -ForegroundColor Yellow
Write-Host " \___/ \___|\__\___/| .__/ \__,_|___/" -ForegroundColor White Write-Host " \___/ \___|\__\___/| .__/ \__,_|___/" -ForegroundColor White
Write-Host " |_|" -ForegroundColor White Write-Host " |_|" -ForegroundColor White
Write-Host "" Write-Host ""
Write-Host "Octopus includes free models, to start:" -ForegroundColor DarkGray Write-Host "Octopus includes free models, to start:" -ForegroundColor DarkGray
Write-Host "" Write-Host ""
Write-Host " cd <project> " -NoNewline; Write-Host "# Open directory" -ForegroundColor DarkGray Write-Host " cd <project> " -NoNewline; Write-Host "# Open directory" -ForegroundColor DarkGray
Write-Host " octopus " -NoNewline; Write-Host "# Run command" -ForegroundColor DarkGray Write-Host " octopus " -NoNewline; Write-Host "# Run command" -ForegroundColor DarkGray
Write-Host "" Write-Host ""
Write-Host "For more information visit " -NoNewline -ForegroundColor DarkGray Write-Host "For more information visit " -NoNewline -ForegroundColor DarkGray
Write-Host "https://eightarms.net/docs" Write-Host "https://eightarms.net/docs"
Write-Host "" Write-Host ""
} }
Install-Octopus Install-Octopus
+81 -81
View File
@@ -1,81 +1,81 @@
# Octopus # Octopus
The AI coding agent built for the terminal. The AI coding agent built for the terminal.
## Install ## Install
**Unix (Linux/macOS):** **Unix (Linux/macOS):**
```bash ```bash
curl -fsSL https://eightarms.net/Octopus/octopus-release/raw/branch/main/install | bash curl -fsSL https://eightarms.net/Octopus/octopus-release/raw/branch/main/install | bash
``` ```
**Windows (PowerShell):** **Windows (PowerShell):**
```powershell ```powershell
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
irm https://eightarms.net/Octopus/octopus-release/raw/branch/main/install.ps1 | iex irm https://eightarms.net/Octopus/octopus-release/raw/branch/main/install.ps1 | iex
``` ```
### Manual install ### Manual install
If the one-liner fails with a connection error, the direct connection to `eightarms.net` is likely being filtered on your network. Download the archive for your platform from the [releases page](https://eightarms.net/Octopus/octopus-release/releases/latest) and install it manually instead. If the one-liner fails with a connection error, the direct connection to `eightarms.net` is likely being filtered on your network. Download the archive for your platform from the [releases page](https://eightarms.net/Octopus/octopus-release/releases/latest) and install it manually instead.
**Unix (Linux/macOS):** **Unix (Linux/macOS):**
```bash ```bash
# after downloading octopus-<platform>.tar.gz from the releases page # after downloading octopus-<platform>.tar.gz from the releases page
tar -xzf octopus-linux-x64.tar.gz tar -xzf octopus-linux-x64.tar.gz
sudo mv octopus /usr/local/bin/ sudo mv octopus /usr/local/bin/
octopus --version octopus --version
``` ```
**Windows (PowerShell):** **Windows (PowerShell):**
After downloading `octopus-windows-x64.zip` from the releases page: After downloading `octopus-windows-x64.zip` from the releases page:
```powershell ```powershell
$zip = "$env:USERPROFILE\Downloads\octopus-windows-x64.zip" $zip = "$env:USERPROFILE\Downloads\octopus-windows-x64.zip"
$dest = "$env:USERPROFILE\.octopus\bin" $dest = "$env:USERPROFILE\.octopus\bin"
$tmp = Join-Path $env:TEMP "octopus_manual" $tmp = Join-Path $env:TEMP "octopus_manual"
New-Item -ItemType Directory -Force -Path $dest,$tmp | Out-Null New-Item -ItemType Directory -Force -Path $dest,$tmp | Out-Null
Expand-Archive -Path $zip -DestinationPath $tmp -Force Expand-Archive -Path $zip -DestinationPath $tmp -Force
Copy-Item "$tmp\octopus.exe" "$dest\octopus.exe" -Force Copy-Item "$tmp\octopus.exe" "$dest\octopus.exe" -Force
Remove-Item -Recurse -Force $tmp Remove-Item -Recurse -Force $tmp
$p = [Environment]::GetEnvironmentVariable('Path','User') $p = [Environment]::GetEnvironmentVariable('Path','User')
if ($p -notlike "*$dest*") { [Environment]::SetEnvironmentVariable('Path', ($p.TrimEnd(';') + ";$dest"), 'User') } if ($p -notlike "*$dest*") { [Environment]::SetEnvironmentVariable('Path', ($p.TrimEnd(';') + ";$dest"), 'User') }
& "$dest\octopus.exe" --version & "$dest\octopus.exe" --version
``` ```
Open a **new** terminal and run `octopus`. Open a **new** terminal and run `octopus`.
## Desktop (beta) ## Desktop (beta)
Prefer a GUI? The Octopus desktop app ships Windows and Linux beta builds alongside the CLI: Prefer a GUI? The Octopus desktop app ships Windows and Linux beta builds alongside the CLI:
- **Windows:** download [`octopus-desktop-win-x64.exe`](https://eightarms.net/Octopus/octopus-release/releases/download/desktop-beta/octopus-desktop-win-x64.exe) from the [`desktop-beta` channel](https://eightarms.net/Octopus/octopus-release/releases/tag/desktop-beta) and run the installer. Installed builds auto-update themselves from the same channel. - **Windows:** download [`octopus-desktop-win-x64.exe`](https://eightarms.net/Octopus/octopus-release/releases/download/desktop-beta/octopus-desktop-win-x64.exe) from the [`desktop-beta` channel](https://eightarms.net/Octopus/octopus-release/releases/tag/desktop-beta) and run the installer. Installed builds auto-update themselves from the same channel.
- **Linux (x86_64):** download [`octopus-desktop-linux-x86_64.AppImage`](https://eightarms.net/Octopus/octopus-release/releases/download/desktop-beta/octopus-desktop-linux-x86_64.AppImage) from the same [`desktop-beta` channel](https://eightarms.net/Octopus/octopus-release/releases/tag/desktop-beta), make it executable and run it: - **Linux (x86_64):** download [`octopus-desktop-linux-x86_64.AppImage`](https://eightarms.net/Octopus/octopus-release/releases/download/desktop-beta/octopus-desktop-linux-x86_64.AppImage) from the same [`desktop-beta` channel](https://eightarms.net/Octopus/octopus-release/releases/tag/desktop-beta), make it executable and run it:
```bash ```bash
chmod +x octopus-desktop-linux-x86_64.AppImage chmod +x octopus-desktop-linux-x86_64.AppImage
./octopus-desktop-linux-x86_64.AppImage ./octopus-desktop-linux-x86_64.AppImage
``` ```
The AppImage auto-updates itself from the same channel (`latest-linux.yml`). Prefer a native package? [`octopus-desktop-linux-amd64.deb`](https://eightarms.net/Octopus/octopus-release/releases/download/desktop-beta/octopus-desktop-linux-amd64.deb) and [`octopus-desktop-linux-x86_64.rpm`](https://eightarms.net/Octopus/octopus-release/releases/download/desktop-beta/octopus-desktop-linux-x86_64.rpm) are published on the same channel for first installs only — they do not auto-update (reinstall or switch to the AppImage for updates), and being unsigned they will trip a first-install warning on apt/dnf. The AppImage auto-updates itself from the same channel (`latest-linux.yml`). Prefer a native package? [`octopus-desktop-linux-amd64.deb`](https://eightarms.net/Octopus/octopus-release/releases/download/desktop-beta/octopus-desktop-linux-amd64.deb) and [`octopus-desktop-linux-x86_64.rpm`](https://eightarms.net/Octopus/octopus-release/releases/download/desktop-beta/octopus-desktop-linux-x86_64.rpm) are published on the same channel for first installs only — they do not auto-update (reinstall or switch to the AppImage for updates), and being unsigned they will trip a first-install warning on apt/dnf.
- This is a beta channel — expect rough edges. macOS builds are on the way but not published yet. - This is a beta channel — expect rough edges. macOS builds are on the way but not published yet.
## Documentation ## Documentation
Visit the [documentation](https://eightarms.net/Octopus/octopus/wiki) for usage guides and API reference. Read the workflow overview (in Chinese): [docs/workflow.zh.md](docs/workflow.zh.md) — how Octopus organizes ticket-driven agent work, with links into the guides shipped in this repo.
## Workflow ## Workflow
Curious how Octopus organizes agent work? Read about the **ticket-driven workflow system** (in Chinese): [docs/workflow.zh.md](docs/workflow.zh.md) Curious how Octopus organizes agent work? Read about the **ticket-driven workflow system** (in Chinese): [docs/workflow.zh.md](docs/workflow.zh.md)
## Development ## Development
The source code and issue tracker are available at the [private development repository](https://eightarms.net/Octopus/octopus). The source code and issue tracker are available at the [private development repository](https://eightarms.net/Octopus/octopus).
## License ## License
MIT License — see the source repository for details. MIT License — see the source repository for details.