diff --git a/README.md b/README.md index f85040c..f13c24c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Nivora предлагает открытые рецепты упаковки д официальный репозиторий Stapler и не официальные пакеты upstream-проектов. -**19 пакетов** · **6 категорий** · `amd64`, `arm64` и `all` +**20 пакетов** · **6 категорий** · `amd64`, `arm64` и `all` [![Проверка Nivora](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml/badge.svg)](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml) @@ -63,6 +63,7 @@ stplr info nivora/codex |:--|:--:|:--:|:--| | [Chatbox](https://chatboxai.app/ru) | `1.20.3` | `amd64`, `arm64` | `stplr install nivora/chatbox` | | [Claude](https://code.claude.com/docs/en/desktop-quickstart) | `1.24012.0` | `amd64`, `arm64` | `stplr install nivora/claude-desktop` | +| [ClaudeAlt](docs/packages/claude-alt.md) | `1.24012.0` | `amd64`, `arm64` | `stplr install nivora/claude-alt` | | [Codex](docs/packages/codex.md) | `26.721.31836` | `amd64` | `stplr install nivora/codex` | | [GitHub Desktop](docs/packages/github-desktop.md) | `3.6.3` | `amd64`, `arm64` | `stplr install nivora/github-desktop` | | [OpenCode](https://opencode.ai/) | `1.18.4` | `amd64`, `arm64` | `stplr install nivora/opencode` | diff --git a/claude-alt/.stapler/update-check b/claude-alt/.stapler/update-check new file mode 100755 index 0000000..31aaa9d --- /dev/null +++ b/claude-alt/.stapler/update-check @@ -0,0 +1,4 @@ +#!/bin/bash +set -euo pipefail +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +exec "${root}/tools/package_updates.sh" check claude-alt diff --git a/claude-alt/Staplerfile b/claude-alt/Staplerfile new file mode 100644 index 0000000..bdd74ba --- /dev/null +++ b/claude-alt/Staplerfile @@ -0,0 +1,134 @@ +name='claude-alt' +version=1.24012.0 +release=1 +summary='Independent secondary Claude desktop application' +summary_ru='Независимое второе приложение Claude' +group='Development/Tools' +desc='A fully independent secondary Claude Desktop installation with its own runtime, profile, desktop identity and Nivora icons.' +desc_ru='Полностью независимая вторая установка Claude Desktop с собственным runtime, профилем, desktop-идентичностью и иконками Nivora.' +homepage='https://github.com/Cheviiot/Nivora' +maintainer='Nivora ' +architectures=('amd64' 'arm64') +license=('Custom') +nonfree=1 +nonfree_msg='ClaudeAlt использует проприетарный Claude Desktop и условия Anthropic.' +nonfree_url='https://www.anthropic.com/legal/consumer-terms' +provides=() +replaces=('claude-alt') +conflicts=() +auto_reqprov_method="dirty" +auto_req=0 +auto_prov=0 + +deps=() +deps_debian=('libgtk-3-0' 'libnotify4' 'libnss3' 'xdg-utils' 'libatspi2.0-0' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libsecret-1-0' 'libc6' 'libxtst6' 'libuuid1' 'xdg-desktop-portal') +deps_ubuntu=("${deps_debian[@]}") +deps_fedora=('gtk3' 'libnotify' 'nss' 'xdg-utils' 'at-spi2-core' 'libdrm' 'mesa-libgbm' 'libxcb' 'libsecret' 'glibc' 'libXtst' 'libuuid' 'xdg-desktop-portal') +deps_arch=('gtk3' 'libnotify' 'nss' 'xdg-utils' 'at-spi2-core' 'libdrm' 'libgbm' 'libxcb' 'libsecret' 'glibc' 'libxtst' 'libutil-linux' 'xdg-desktop-portal') +deps_opensuse=('gtk3' 'libnotify4' 'mozilla-nss' 'xdg-utils' 'at-spi2-core' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libsecret-1-0' 'glibc' 'libXtst6' 'libuuid1' 'xdg-desktop-portal') +deps_altlinux=('libgtk+3' 'libnotify' 'libnss' 'xdg-utils' 'libat-spi2-core' 'libdrm' 'libgbm' 'libxcb' 'libsecret' 'glibc' 'libXtst' 'libuuid' 'xdg-desktop-portal') + +build_deps=('binutils') +build_deps_debian=('binutils' 'python3') +build_deps_ubuntu=("${build_deps_debian[@]}") +build_deps_fedora=('binutils' 'python3') +build_deps_arch=('binutils' 'python') +build_deps_opensuse=('binutils' 'python3') +build_deps_altlinux=('binutils' 'python3') + +sources=( + "https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_${version}_amd64.deb?~archive=false&~name=claude-desktop.deb" + "local:///claude-alt" + "https://raw.githubusercontent.com/Cheviiot/Nivora/main/claude-desktop/claude-alt.png?~archive=false&~name=claude-alt.png" + "https://raw.githubusercontent.com/Cheviiot/Nivora/main/claude-desktop/claude-alt-tray-turquoise.png?~archive=false&~name=claude-alt-tray.png" + "local:///patch-asar.py" +) +sources_arm64=( + "https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_${version}_arm64.deb?~archive=false&~name=claude-desktop.deb" + "local:///claude-alt" + "https://raw.githubusercontent.com/Cheviiot/Nivora/main/claude-desktop/claude-alt.png?~archive=false&~name=claude-alt.png" + "https://raw.githubusercontent.com/Cheviiot/Nivora/main/claude-desktop/claude-alt-tray-turquoise.png?~archive=false&~name=claude-alt-tray.png" + "local:///patch-asar.py" +) +checksums=( + 'sha256:1096a8063956f8430fdbe504bb1c5e02985f31d0f45d082e4966e5d30ea7e3ac' + 'sha256:b5264918d7cc3436bde057057e5790f617e42ccfcd1237d9170d26aea12799b8' + 'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4' + 'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e' + 'sha256:14e0a5ce4da6afb25b13b3cee0b0264fbd896430dae85ade1f3394d8ca999e7d' +) +checksums_arm64=( + 'sha256:37dd5dc1b0bfcb3192561acbd33cb59037806fe9b95221a3ddf38244203196f1' + 'sha256:b5264918d7cc3436bde057057e5790f617e42ccfcd1237d9170d26aea12799b8' + 'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4' + 'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e' + 'sha256:14e0a5ce4da6afb25b13b3cee0b0264fbd896430dae85ade1f3394d8ca999e7d' +) + +scripts=( + ['postinstall']='postinstall.sh' + ['postremove']='postremove.sh' +) + +package() { + cd "${srcdir}" + mkdir -p extracted + ar x claude-desktop.deb + tar -xf data.tar.* -C extracted + install -dm755 "${pkgdir}/usr/lib" + cp -a extracted/usr/lib/claude-desktop "${pkgdir}/usr/lib/claude-alt" + mv "${pkgdir}/usr/lib/claude-alt/claude-desktop" \ + "${pkgdir}/usr/lib/claude-alt/claude-alt-bin" + chmod 0755 "${pkgdir}/usr/lib/claude-alt/claude-alt-bin" \ + "${pkgdir}/usr/lib/claude-alt/chrome-sandbox" + python3 patch-asar.py "${pkgdir}/usr/lib/claude-alt/resources/app.asar" + install -Dm755 claude-alt "${pkgdir}/usr/lib/claude-alt/claude-alt" + install -Dm644 claude-alt.png "${pkgdir}/usr/lib/claude-alt/resources/icon.png" + for icon in TrayIconLinux.png TrayIconLinux-Dark.png; do + install -Dm644 claude-alt-tray.png \ + "${pkgdir}/usr/lib/claude-alt/resources/${icon}" + done + install -dm755 "${pkgdir}/usr/bin" + ln -s /usr/lib/claude-alt/claude-alt "${pkgdir}/usr/bin/claude-alt" + ln -s claude-alt "${pkgdir}/usr/bin/claude-code-alt" + install -Dm644 claude-alt.png \ + "${pkgdir}/usr/share/icons/hicolor/512x512/apps/claude-alt.png" + install -Dm644 /dev/stdin \ + "${pkgdir}/usr/share/applications/com.anthropic.ClaudeAlt.desktop" <<'EOF' +[Desktop Entry] +Name=ClaudeAlt +Name[ru]=ClaudeAlt +Comment=Independent secondary Claude desktop application +Comment[ru]=Независимое второе приложение Claude +GenericName=AI Assistant +Keywords=AI;Chat;Assistant;Claude;Code;LLM; +Exec=/usr/bin/claude-alt %U +Icon=claude-alt +Type=Application +StartupNotify=true +StartupWMClass=com.anthropic.ClaudeAlt +SingleMainWindow=true +Categories=Development; +Actions=NewChat;NewCode; + +[Desktop Action NewChat] +Name=New chat +Name[ru]=Новый чат +Exec=/usr/bin/claude-alt claude://claude.ai/new + +[Desktop Action NewCode] +Name=New Claude Code session +Name[ru]=Новая сессия Claude Code +Exec=/usr/bin/claude-alt claude://code/new +EOF + install-license extracted/usr/share/doc/claude-desktop/copyright \ + "claude-alt/copyright" +} + +files() { + files-find "/usr/lib/claude-alt/**/*" + files-find-binary + files-find-desktop + files-find-license + files-find "/usr/share/icons/hicolor/*/apps/claude-alt.png" +} diff --git a/claude-alt/claude-alt b/claude-alt/claude-alt new file mode 100755 index 0000000..84d84c8 --- /dev/null +++ b/claude-alt/claude-alt @@ -0,0 +1,36 @@ +#!/bin/sh +set -eu + +if [ -z "${HOME:-}" ] && [ -z "${XDG_CONFIG_HOME:-}" ]; then + echo "claude-alt: HOME or XDG_CONFIG_HOME is required" >&2 + exit 1 +fi + +config_home="${XDG_CONFIG_HOME:-${HOME}/.config}" +default_data_dir="${config_home}/ClaudeAlt" +legacy_data_dir="${CLAUDE_DESKTOP_ACCOUNT2_DIR:-${config_home}/Claude-Account-2}" +claude_alt_executable="${CLAUDE_ALT_EXECUTABLE:-/usr/lib/claude-alt/claude-alt-bin}" + +if [ -n "${CLAUDE_ALT_DATA_DIR:-}" ]; then + data_dir="${CLAUDE_ALT_DATA_DIR}" +elif [ -d "${default_data_dir}" ]; then + data_dir="${default_data_dir}" +elif [ -d "${legacy_data_dir}" ]; then + data_dir="${legacy_data_dir}" +else + data_dir="${default_data_dir}" +fi + +if [ ! -x "${claude_alt_executable}" ]; then + echo "claude-alt: executable not found: ${claude_alt_executable}" >&2 + exit 1 +fi + +mkdir -p "${data_dir}" +CHROME_DESKTOP='com.anthropic.ClaudeAlt.desktop' +export CHROME_DESKTOP + +exec "${claude_alt_executable}" \ + --user-data-dir="${data_dir}" \ + --class=com.anthropic.ClaudeAlt \ + "$@" diff --git a/claude-alt/patch-asar.py b/claude-alt/patch-asar.py new file mode 100755 index 0000000..df85199 --- /dev/null +++ b/claude-alt/patch-asar.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Give ClaudeAlt an independent Electron identity without changing its protocol.""" + +import copy +import hashlib +import json +import struct +import sys +from pathlib import Path + +path = Path(sys.argv[1]) +data = path.read_bytes() +if len(data) < 16: + raise SystemExit("ClaudeAlt app.asar is too small") + +_, _, _, header_size = struct.unpack(" dict: + blocks = [ + hashlib.sha256(content[index : index + block_size]).hexdigest() + for index in range(0, len(content), block_size) + ] or [hashlib.sha256(b"").hexdigest()] + return { + "algorithm": "SHA256", + "hash": hashlib.sha256(content).hexdigest(), + "blockSize": block_size, + "blocks": blocks, + } + + +def rebuild(node: dict, prefix: str = "") -> dict: + result = {} + for name, child in node.get("files", {}).items(): + item_path = f"{prefix}/{name}" if prefix else name + if "files" in child: + result[name] = { + **{key: value for key, value in child.items() if key != "files"}, + "files": rebuild(child, item_path), + } + continue + + entry = copy.deepcopy(child) + if entry.get("unpacked"): + result[name] = entry + continue + + offset, size = int(entry["offset"]), int(entry["size"]) + content = data[base_offset + offset : base_offset + offset + size] + changed = False + + if item_path == "package.json": + package = json.loads(content) + expected = { + "productName": "Claude", + "desktopName": "com.anthropic.Claude.desktop", + } + for field, value in expected.items(): + if package.get(field) != value: + raise SystemExit( + f"unexpected Claude package.json {field}: " + f"{package.get(field)!r} != {value!r}" + ) + package["productName"] = "ClaudeAlt" + package["desktopName"] = "com.anthropic.ClaudeAlt.desktop" + content = (json.dumps(package, indent=2) + "\n").encode() + counts["package"] += 1 + changed = True + + old_socket = b"claude-cowork-vm.sock" + socket_count = content.count(old_socket) + if socket_count: + content = content.replace(old_socket, b"claude-alt-cowork-vm.sock") + counts["socket"] += socket_count + changed = True + + marker = b'P.app.isPackaged||P.app.setName("Claude");RRn();' + marker_count = content.count(marker) + if marker_count: + content = content.replace( + marker, + b'P.app.isPackaged||P.app.setName("Claude");' + b'P.app.userAgentFallback=P.app.userAgentFallback' + b'.replaceAll("ClaudeAlt","Claude");RRn();', + ) + counts["user_agent"] += marker_count + changed = True + + entry["offset"] = str(len(output)) + if changed: + entry["size"] = len(content) + block_size = entry.get("integrity", {}).get("blockSize", 4194304) + entry["integrity"] = integrity(content, block_size) + output.extend(content) + result[name] = entry + return result + + +rebuilt_header = {"files": rebuild(header)} +if counts != {"package": 1, "socket": 1, "user_agent": 1}: + raise SystemExit(f"unexpected ClaudeAlt patch counts: {counts}") + +header_bytes = json.dumps(rebuilt_header, separators=(",", ":")).encode() +padded_size = (len(header_bytes) + 3) & ~3 +patched = ( + struct.pack("&2 + exit 1 +fi +if [[ -L /proc/self/ns/user ]] && command -v unshare >/dev/null 2>&1 \ + && unshare --user true 2>/dev/null; then + chmod 0755 "$sandbox" +else + chmod 4755 "$sandbox" +fi + +if command -v update-desktop-database >/dev/null 2>&1; then + update-desktop-database -q /usr/share/applications || true +fi +if command -v gtk-update-icon-cache >/dev/null 2>&1; then + gtk-update-icon-cache -f -q /usr/share/icons/hicolor || true +fi diff --git a/claude-alt/postremove.sh b/claude-alt/postremove.sh new file mode 100755 index 0000000..c18930b --- /dev/null +++ b/claude-alt/postremove.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -euo pipefail + +if command -v update-desktop-database >/dev/null 2>&1; then + update-desktop-database -q /usr/share/applications || true +fi +if command -v gtk-update-icon-cache >/dev/null 2>&1; then + gtk-update-icon-cache -f -q /usr/share/icons/hicolor || true +fi diff --git a/claude-alt/stapler-repo.toml b/claude-alt/stapler-repo.toml new file mode 100644 index 0000000..9c222ce --- /dev/null +++ b/claude-alt/stapler-repo.toml @@ -0,0 +1 @@ +include = "../stapler-repo.toml" diff --git a/claude-desktop/Staplerfile b/claude-desktop/Staplerfile index 142ffaf..00af455 100644 --- a/claude-desktop/Staplerfile +++ b/claude-desktop/Staplerfile @@ -1,6 +1,6 @@ name='claude-desktop' version=1.24012.0 -release=4 +release=5 summary='Claude desktop application' summary_ru='Десктопное приложение Claude' group='Development/Tools' @@ -50,37 +50,25 @@ build_deps_altlinux=('binutils' 'python3') sources=( "https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_${version}_amd64.deb?~archive=false&~name=claude-desktop.deb" - "local:///claude-alt" - "local:///claude-alt.png" "local:///claude-tray-orange.png" - "local:///claude-alt-tray-turquoise.png" 'local:///LICENSE' ) sources_arm64=( "https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_${version}_arm64.deb?~archive=false&~name=claude-desktop.deb" - "local:///claude-alt" - "local:///claude-alt.png" "local:///claude-tray-orange.png" - "local:///claude-alt-tray-turquoise.png" 'local:///LICENSE' ) checksums=( 'sha256:1096a8063956f8430fdbe504bb1c5e02985f31d0f45d082e4966e5d30ea7e3ac' - 'sha256:00d9fe6549d82f564a99c6e8ec54d1380fdf8f0a20c99bb9b3cda1f689adf57d' - 'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4' 'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b' - 'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e' 'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f' ) checksums_arm64=( 'sha256:37dd5dc1b0bfcb3192561acbd33cb59037806fe9b95221a3ddf38244203196f1' - 'sha256:00d9fe6549d82f564a99c6e8ec54d1380fdf8f0a20c99bb9b3cda1f689adf57d' - 'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4' 'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b' - 'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e' 'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f' ) @@ -111,208 +99,6 @@ package() { "${pkgdir}/usr/lib/claude-desktop/resources/${icon_name}" done - # ClaudeAlt получает отдельные executable и app.asar. Остальные неизменяемые - # Electron-файлы подключаются к основной копии относительными symlink-ами. - # Это сохраняет отдельный resourcesPath, но не удваивает пакет целиком. - install -dm755 \ - "${pkgdir}/usr/lib/claude-alt" \ - "${pkgdir}/usr/lib/claude-alt/resources" - cp -p "${pkgdir}/usr/lib/claude-desktop/claude-desktop" \ - "${pkgdir}/usr/lib/claude-alt/claude-alt-bin" - - for entry in "${pkgdir}/usr/lib/claude-desktop"/*; do - entry_name="${entry##*/}" - case "${entry_name}" in - claude-desktop | resources) continue ;; - esac - ln -s "../claude-desktop/${entry_name}" \ - "${pkgdir}/usr/lib/claude-alt/${entry_name}" - done - - for entry in "${pkgdir}/usr/lib/claude-desktop/resources"/*; do - entry_name="${entry##*/}" - case "${entry_name}" in - app.asar) - cp -p "${entry}" "${pkgdir}/usr/lib/claude-alt/resources/app.asar" - ;; - icon.png | TrayIconLinux.png | TrayIconLinux-Dark.png) - # Эти ресурсы ниже заменяются иконкой ClaudeAlt. - ;; - *) - ln -s "../../claude-desktop/resources/${entry_name}" \ - "${pkgdir}/usr/lib/claude-alt/resources/${entry_name}" - ;; - esac - done - - # Большая иконка ClaudeAlt используется окном, а в tray устанавливается - # отдельный бирюзовый вариант штатного глифа Claude. - install -Dm644 "${srcdir}/claude-alt.png" \ - "${pkgdir}/usr/lib/claude-alt/resources/icon.png" - for icon_name in TrayIconLinux.png TrayIconLinux-Dark.png; do - install -Dm644 "${srcdir}/claude-alt-tray-turquoise.png" \ - "${pkgdir}/usr/lib/claude-alt/resources/${icon_name}" - done - - alt_asar="${pkgdir}/usr/lib/claude-alt/resources/app.asar" - - python3 - "${alt_asar}" <<'PY' -import copy -import hashlib -import json -import struct -import sys -from pathlib import Path - -asar_path = Path(sys.argv[1]) -data = asar_path.read_bytes() -if len(data) < 16: - raise SystemExit("ClaudeAlt app.asar is too small") - -_, _, _, header_size = struct.unpack(" dict: - blocks = [ - hashlib.sha256(content[index : index + block_size]).hexdigest() - for index in range(0, len(content), block_size) - ] - if not blocks: - blocks = [hashlib.sha256(b"").hexdigest()] - return { - "algorithm": "SHA256", - "hash": hashlib.sha256(content).hexdigest(), - "blockSize": block_size, - "blocks": blocks, - } - - -def rebuild_node(node: dict, prefix: str = "") -> dict: - global patched_cowork_socket, patched_package_json, patched_user_agent - - rebuilt = {} - for name, child in node.get("files", {}).items(): - path = f"{prefix}/{name}" if prefix else name - if "files" in child: - rebuilt[name] = { - **{key: value for key, value in child.items() if key != "files"}, - "files": rebuild_node(child, path), - } - continue - - entry = copy.deepcopy(child) - if entry.get("unpacked"): - rebuilt[name] = entry - continue - - offset = int(entry["offset"]) - size = int(entry["size"]) - content = data[base_offset + offset : base_offset + offset + size] - content_changed = False - - if path == "package.json": - package_json = json.loads(content) - expected = { - "productName": "Claude", - "desktopName": "com.anthropic.Claude.desktop", - } - for field, expected_value in expected.items(): - if package_json.get(field) != expected_value: - raise SystemExit( - f"unexpected Claude package.json {field}: " - f"{package_json.get(field)!r} != {expected_value!r}" - ) - # Отдельный productName нужен Electron для независимого instance - # lock. Официальный User-Agent восстанавливается ниже отдельно. - package_json["productName"] = "ClaudeAlt" - package_json["desktopName"] = "com.anthropic.ClaudeAlt.desktop" - content = (json.dumps(package_json, indent=2) + "\n").encode("utf-8") - patched_package_json += 1 - content_changed = True - - socket_name = b"claude-cowork-vm.sock" - socket_matches = content.count(socket_name) - if socket_matches: - content = content.replace(socket_name, b"claude-alt-cowork-vm.sock") - patched_cowork_socket += socket_matches - content_changed = True - - user_agent_marker = ( - b'P.app.isPackaged||P.app.setName("Claude");RRn();' - ) - user_agent_matches = content.count(user_agent_marker) - if user_agent_matches: - content = content.replace( - user_agent_marker, - b'P.app.isPackaged||P.app.setName("Claude");' - b'P.app.userAgentFallback=P.app.userAgentFallback' - b'.replaceAll("ClaudeAlt","Claude");RRn();', - ) - patched_user_agent += user_agent_matches - content_changed = True - - entry["offset"] = str(len(output)) - if content_changed: - entry["size"] = len(content) - entry["integrity"] = integrity_for( - content, - entry.get("integrity", {}).get("blockSize", 4194304), - ) - output.extend(content) - rebuilt[name] = entry - - return rebuilt - - -rebuilt_header = {"files": rebuild_node(header)} -if patched_package_json != 1: - raise SystemExit( - f"expected one ClaudeAlt package.json, got {patched_package_json}" - ) -if patched_cowork_socket != 1: - raise SystemExit( - f"expected one ClaudeAlt Cowork socket, got {patched_cowork_socket}" - ) -if patched_user_agent != 1: - raise SystemExit( - f"expected one ClaudeAlt User-Agent patch, got {patched_user_agent}" - ) - -header_bytes = json.dumps(rebuilt_header, separators=(",", ":")).encode("utf-8") -header_padded_size = (len(header_bytes) + 3) & ~3 -patched_data = ( - struct.pack( - "&2 + exit 1 +fi +grep -Fq 'python3 patch-asar.py' "$alternate_recipe" +grep -Fq 'com.anthropic.ClaudeAlt.desktop' "$alternate_recipe" +python3 -m py_compile "${repo_root}/claude-alt/patch-asar.py" + mkdir -p "${temp_dir}/bin" "${temp_dir}/home" log="${temp_dir}/launch.log" diff --git a/tools/package_updates.sh b/tools/package_updates.sh index cf43f85..0b74f8b 100755 --- a/tools/package_updates.sh +++ b/tools/package_updates.sh @@ -7,6 +7,7 @@ readonly -a PACKAGES=( balena-etcher chatbox clash-verge-rev + claude-alt claude-desktop codex fisher @@ -218,7 +219,7 @@ latest_version() { balena-etcher) github_latest_release balena-io/etcher ;; chatbox) latest_chatbox ;; clash-verge-rev) github_latest_release clash-verge-rev/clash-verge-rev ;; - claude-desktop) latest_claude_desktop ;; + claude-alt | claude-desktop) latest_claude_desktop ;; codex) github_latest_release Boria138/codex-app-linux ;; fisher) github_latest_release jorgebucaran/fisher ;; github-desktop) diff --git a/tools/validate_repo.py b/tools/validate_repo.py index 2e5fb4c..683cafa 100755 --- a/tools/validate_repo.py +++ b/tools/validate_repo.py @@ -17,6 +17,7 @@ EXPECTED_PACKAGES = ( "balena-etcher", "chatbox", "clash-verge-rev", + "claude-alt", "claude-desktop", "codex", "fisher", @@ -43,6 +44,7 @@ REQUIRED_ROOT_FILES = { Path("docs/maintenance.md"), Path("docs/security-model.md"), Path("docs/packages/claude-desktop.md"), + Path("docs/packages/claude-alt.md"), Path("docs/packages/codex.md"), Path("docs/packages/github-desktop.md"), Path("docs/packages/opencode.md"),