iStoreOS 的 vs code server 网页版本安装 AI copilot

肯定要先安装 codeserver

进到 iStoreOS 的控制台,输入:

docker exec -it codeserver bash

然后下载下面的脚本,放到 /mnt/xxx 的硬盘目录下:

#!/usr/bin/env bash

export PATH=$PATH:/app/code-server/bin

# Extract VS Code version from code-server
get_vscode_version() {
    code-server --version 2>/dev/null | grep "with Code" | sed 's/.*with Code //'
}

# Find compatible extension version
find_compatible_version() {
    local extension_id="$1"
    local vscode_version="$2"

    local response
    response=$(curl -s -X POST "https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery" \
        -H "Content-Type: application/json" \
        -H "Accept: application/json;api-version=3.0-preview.1" \
        -d "{
            \"filters\": [{
                \"criteria\": [
                    {\"filterType\": 7, \"value\": \"$extension_id\"},
                    {\"filterType\": 12, \"value\": \"4096\"}
                ],
                \"pageSize\": 50
            }],
            \"flags\": 4112
        }")

    echo "$response" | jq -r --arg vscode_version "$vscode_version" '
        .results[0].extensions[0].versions[] |
        select(.version | test("^[0-9]+\\.[0-9]+\\.[0-9]*$")) |
        select(.version | length < 8) |
        {
            version: .version,
            engine: (.properties[] | select(.key == "Microsoft.VisualStudio.Code.Engine") | .value)
        } |
        select(.engine | ltrimstr("^") | split(".") |
            map(split("-")[0] | tonumber?) as $engine_parts |
            ($vscode_version | split(".") | map(tonumber)) as $vscode_parts |
            (
                ($engine_parts[0] // 0) < $vscode_parts[0] or
                (($engine_parts[0] // 0) == $vscode_parts[0] and ($engine_parts[1] // 0) < $vscode_parts[1]) or
                (($engine_parts[0] // 0) == $vscode_parts[0] and ($engine_parts[1] // 0) == $vscode_parts[1] and ($engine_parts[2] // 0) <= $vscode_parts[2])
            )
        ) |
        .version' | head -n 1
}

# Install extension
install_extension() {
    local extension_id="$1"
    local version="$2"
    local user_data_dir="$3"
    local extensions_dir="$4"
    local extension_name
    extension_name=$(echo "$extension_id" | cut -d'.' -f2)
    local temp_dir="/tmp/code-extensions"

    echo "Installing $extension_id v$version..."

    # Create temp directory
    mkdir -p "$temp_dir"

    # Download
    echo "  Downloading..."
    curl -L "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/GitHub/vsextensions/$extension_name/$version/vspackage" \
        -o "$temp_dir/$extension_name.vsix.gz"

    if [ ! -f "$temp_dir/$extension_name.vsix.gz" ]; then
        echo "  ✗ Download failed for $extension_id"
        return 1
    fi

    # Decompress (handle both gunzip and gzip -d)
    if command -v gunzip >/dev/null 2>&1; then
        gunzip -f "$temp_dir/$extension_name.vsix.gz"
    else
        gzip -df "$temp_dir/$extension_name.vsix.gz"
    fi

    # Ensure extensions dir exists if provided
    if [ -n "$extensions_dir" ]; then
        mkdir -p "$extensions_dir" 2>/dev/null || true
    fi

    # Build install command with optional user-data-dir and extensions-dir
    local install_cmd=(code-server --force)
    if [ -n "$user_data_dir" ]; then
        install_cmd+=("--user-data-dir=$user_data_dir")
    fi
    if [ -n "$extensions_dir" ]; then
        install_cmd+=("--extensions-dir=$extensions_dir")
    fi
    install_cmd+=("--install-extension" "$temp_dir/$extension_name.vsix")

    # Execute install
    "${install_cmd[@]}"

    # Clean up
    rm -f "$temp_dir/$extension_name.vsix"

    echo "  ✓ $extension_id installed successfully!"
    return 0
}

# Check for required dependencies
check_dependencies() {
    local missing_deps=()

    # Check for required commands
    for cmd in curl jq code-server; do
        if ! command -v "$cmd" >/dev/null 2>&1; then
            missing_deps+=("$cmd")
        fi
    done

    # Check for either gunzip or gzip
    if ! command -v gunzip >/dev/null 2>&1 && ! command -v gzip >/dev/null 2>&1; then
        missing_deps+=("gunzip/gzip")
    fi

    if [ "${#missing_deps[@]}" -gt 0 ]; then
        echo "Error: Missing required dependencies: ${missing_deps[*]}"
        echo "Please install the missing dependencies and try again."
        exit 1
    fi
}

# Main script
echo "GitHub Copilot Extensions Installer"
echo "===================================="
echo ""

# Check dependencies
check_dependencies

# Get VS Code version
VSCODE_VERSION="$(get_vscode_version)"

if [ -z "$VSCODE_VERSION" ]; then
    echo "Error: Could not extract VS Code version from code-server"
    exit 1
fi

echo "Detected VS Code version: $VSCODE_VERSION"

# For this environment, use fixed data and extensions directories
USER_DATA_DIR="/config/data"
EXTENSIONS_DIR="/config/extensions"

echo "Using USER_DATA_DIR=$USER_DATA_DIR"
echo "Using EXTENSIONS_DIR=$EXTENSIONS_DIR"

# Ensure directories exist and have correct permissions where possible
mkdir -p "$USER_DATA_DIR" 2>/dev/null || true
mkdir -p "$EXTENSIONS_DIR" 2>/dev/null || true

# Extensions to install
# Use portable array declaration
EXTENSIONS="GitHub.copilot GitHub.copilot-chat"
FAILED=0

# Iterate through space-separated list for portability
for ext in $EXTENSIONS; do
    echo "Processing $ext..."

    # Find compatible version
    version="$(find_compatible_version "$ext" "$VSCODE_VERSION")"

    if [ -z "$version" ]; then
        echo "  ✗ No compatible version found for $ext"
        FAILED="$((FAILED + 1))"
    else
        echo "  Found compatible version: $version"
        if ! install_extension "$ext" "$version" "$USER_DATA_DIR" "$EXTENSIONS_DIR"; then
            FAILED="$((FAILED + 1))"
        fi
    fi
    echo ""
done

# Summary
echo "===================================="
if [ $FAILED -eq 0 ]; then
    echo "✓ All extensions installed successfully!"
    # Clean up temp directory on success
    rm -rf /tmp/code-extensions
else
    echo "⚠ Completed with $FAILED error(s)"
    exit 1
fi

然后到刚刚的控制台,运行脚本:

/mnt/xxx/install-copilot.sh

然后咱们的 codeserver 就可以运行 copiot AI 了。

参考文章: