mirror of
https://github.com/Cheviiot/Nivora.git
synced 2026-08-03 15:51:12 +00:00
feat: split ClaudeAlt into independent package
This commit is contained in:
@@ -11,7 +11,7 @@ Nivora предлагает открытые рецепты упаковки д
|
|||||||
официальный репозиторий Stapler и не официальные пакеты upstream-проектов.
|
официальный репозиторий Stapler и не официальные пакеты upstream-проектов.
|
||||||
|
|
||||||
<!-- package-count -->
|
<!-- package-count -->
|
||||||
**19 пакетов** · **6 категорий** · `amd64`, `arm64` и `all`
|
**20 пакетов** · **6 категорий** · `amd64`, `arm64` и `all`
|
||||||
|
|
||||||
[](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml)
|
[](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` |
|
| [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` |
|
| [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` |
|
| [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` |
|
| [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` |
|
| [OpenCode](https://opencode.ai/) | `1.18.4` | `amd64`, `arm64` | `stplr install nivora/opencode` |
|
||||||
|
|||||||
Executable
+4
@@ -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
|
||||||
@@ -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 <https://github.com/Cheviiot/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"
|
||||||
|
}
|
||||||
Executable
+36
@@ -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 \
|
||||||
|
"$@"
|
||||||
Executable
+123
@@ -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("<IIII", data[:16])
|
||||||
|
header = json.loads(data[16 : 16 + header_size])
|
||||||
|
base_offset = 16 + ((header_size + 3) & ~3)
|
||||||
|
output = bytearray()
|
||||||
|
counts = {"package": 0, "socket": 0, "user_agent": 0}
|
||||||
|
|
||||||
|
|
||||||
|
def integrity(content: bytes, block_size: int) -> 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("<IIII", 4, padded_size + 8, padded_size + 4, len(header_bytes))
|
||||||
|
+ header_bytes
|
||||||
|
+ b"\0" * (padded_size - len(header_bytes))
|
||||||
|
+ bytes(output)
|
||||||
|
)
|
||||||
|
required = (
|
||||||
|
b"com.anthropic.ClaudeAlt.desktop",
|
||||||
|
b'"productName": "ClaudeAlt"',
|
||||||
|
b'.replaceAll("ClaudeAlt","Claude")',
|
||||||
|
b"claude-alt-cowork-vm.sock",
|
||||||
|
)
|
||||||
|
if any(patched.count(value) != 1 for value in required):
|
||||||
|
raise SystemExit("ClaudeAlt identity verification failed")
|
||||||
|
path.write_bytes(patched)
|
||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
sandbox=/usr/lib/claude-alt/chrome-sandbox
|
||||||
|
if [[ ! -f "$sandbox" ]]; then
|
||||||
|
echo "Ошибка: не найден Electron sandbox: ${sandbox}" >&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
|
||||||
Executable
+9
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
include = "../stapler-repo.toml"
|
||||||
+2
-252
@@ -1,6 +1,6 @@
|
|||||||
name='claude-desktop'
|
name='claude-desktop'
|
||||||
version=1.24012.0
|
version=1.24012.0
|
||||||
release=4
|
release=5
|
||||||
summary='Claude desktop application'
|
summary='Claude desktop application'
|
||||||
summary_ru='Десктопное приложение Claude'
|
summary_ru='Десктопное приложение Claude'
|
||||||
group='Development/Tools'
|
group='Development/Tools'
|
||||||
@@ -50,37 +50,25 @@ build_deps_altlinux=('binutils' 'python3')
|
|||||||
|
|
||||||
sources=(
|
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"
|
"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-tray-orange.png"
|
||||||
"local:///claude-alt-tray-turquoise.png"
|
|
||||||
'local:///LICENSE'
|
'local:///LICENSE'
|
||||||
)
|
)
|
||||||
|
|
||||||
sources_arm64=(
|
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"
|
"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-tray-orange.png"
|
||||||
"local:///claude-alt-tray-turquoise.png"
|
|
||||||
'local:///LICENSE'
|
'local:///LICENSE'
|
||||||
)
|
)
|
||||||
|
|
||||||
checksums=(
|
checksums=(
|
||||||
'sha256:1096a8063956f8430fdbe504bb1c5e02985f31d0f45d082e4966e5d30ea7e3ac'
|
'sha256:1096a8063956f8430fdbe504bb1c5e02985f31d0f45d082e4966e5d30ea7e3ac'
|
||||||
'sha256:00d9fe6549d82f564a99c6e8ec54d1380fdf8f0a20c99bb9b3cda1f689adf57d'
|
|
||||||
'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4'
|
|
||||||
'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b'
|
'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b'
|
||||||
'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e'
|
|
||||||
'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f'
|
'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f'
|
||||||
)
|
)
|
||||||
|
|
||||||
checksums_arm64=(
|
checksums_arm64=(
|
||||||
'sha256:37dd5dc1b0bfcb3192561acbd33cb59037806fe9b95221a3ddf38244203196f1'
|
'sha256:37dd5dc1b0bfcb3192561acbd33cb59037806fe9b95221a3ddf38244203196f1'
|
||||||
'sha256:00d9fe6549d82f564a99c6e8ec54d1380fdf8f0a20c99bb9b3cda1f689adf57d'
|
|
||||||
'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4'
|
|
||||||
'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b'
|
'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b'
|
||||||
'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e'
|
|
||||||
'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f'
|
'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,208 +99,6 @@ package() {
|
|||||||
"${pkgdir}/usr/lib/claude-desktop/resources/${icon_name}"
|
"${pkgdir}/usr/lib/claude-desktop/resources/${icon_name}"
|
||||||
done
|
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("<IIII", data[:16])
|
|
||||||
header = json.loads(data[16 : 16 + header_size])
|
|
||||||
base_offset = 16 + ((header_size + 3) & ~3)
|
|
||||||
output = bytearray()
|
|
||||||
patched_package_json = 0
|
|
||||||
patched_cowork_socket = 0
|
|
||||||
patched_user_agent = 0
|
|
||||||
|
|
||||||
|
|
||||||
def integrity_for(content: bytes, block_size: int = 4194304) -> 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(
|
|
||||||
"<IIII",
|
|
||||||
4,
|
|
||||||
header_padded_size + 8,
|
|
||||||
header_padded_size + 4,
|
|
||||||
len(header_bytes),
|
|
||||||
)
|
|
||||||
+ header_bytes
|
|
||||||
+ (b"\0" * (header_padded_size - len(header_bytes)))
|
|
||||||
+ bytes(output)
|
|
||||||
)
|
|
||||||
if patched_data.count(b"com.anthropic.ClaudeAlt.desktop") != 1:
|
|
||||||
raise SystemExit("ClaudeAlt desktopName was not written exactly once")
|
|
||||||
if patched_data.count(b'"productName": "ClaudeAlt"') != 1:
|
|
||||||
raise SystemExit("ClaudeAlt productName was not written exactly once")
|
|
||||||
if patched_data.count(b'.replaceAll("ClaudeAlt","Claude")') != 1:
|
|
||||||
raise SystemExit("ClaudeAlt official User-Agent patch is missing")
|
|
||||||
if patched_data.count(b"claude-alt-cowork-vm.sock") != 1:
|
|
||||||
raise SystemExit("ClaudeAlt Cowork socket was not written exactly once")
|
|
||||||
asar_path.write_bytes(patched_data)
|
|
||||||
PY
|
|
||||||
|
|
||||||
chmod 755 "${pkgdir}/usr/lib/claude-alt/claude-alt-bin"
|
|
||||||
install -Dm755 "${srcdir}/claude-alt" \
|
|
||||||
"${pkgdir}/usr/lib/claude-alt/claude-alt"
|
|
||||||
|
|
||||||
install -dm755 "${pkgdir}/usr/share"
|
install -dm755 "${pkgdir}/usr/share"
|
||||||
cp -a "${srcdir}/extracted/usr/share/applications" "${pkgdir}/usr/share/"
|
cp -a "${srcdir}/extracted/usr/share/applications" "${pkgdir}/usr/share/"
|
||||||
cp -a "${srcdir}/extracted/usr/share/icons" "${pkgdir}/usr/share/"
|
cp -a "${srcdir}/extracted/usr/share/icons" "${pkgdir}/usr/share/"
|
||||||
@@ -327,42 +113,8 @@ PY
|
|||||||
-e 's/^Categories=.*/Categories=Development;/' \
|
-e 's/^Categories=.*/Categories=Development;/' \
|
||||||
"${desktop_file}"
|
"${desktop_file}"
|
||||||
|
|
||||||
install -Dm644 "${srcdir}/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=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=claude-alt claude://claude.ai/new
|
|
||||||
|
|
||||||
[Desktop Action NewCode]
|
|
||||||
Name=New Claude Code session
|
|
||||||
Name[ru]=Новая сессия Claude Code
|
|
||||||
Exec=claude-alt claude://code/new
|
|
||||||
EOF
|
|
||||||
|
|
||||||
install -dm755 "${pkgdir}/usr/bin"
|
install -dm755 "${pkgdir}/usr/bin"
|
||||||
ln -sfn /usr/lib/claude-desktop/claude-desktop "${pkgdir}/usr/bin/claude-desktop"
|
ln -sfn /usr/lib/claude-desktop/claude-desktop "${pkgdir}/usr/bin/claude-desktop"
|
||||||
ln -sfn /usr/lib/claude-alt/claude-alt "${pkgdir}/usr/bin/claude-alt"
|
|
||||||
ln -sfn /usr/lib/claude-alt/claude-alt "${pkgdir}/usr/bin/claude-code-alt"
|
|
||||||
ln -sfn /usr/lib/claude-alt/claude-alt "${pkgdir}/usr/bin/claude-desktop-account2"
|
|
||||||
|
|
||||||
install -dm755 "${pkgdir}/usr/share/licenses/claude-desktop"
|
install -dm755 "${pkgdir}/usr/share/licenses/claude-desktop"
|
||||||
install-license "${srcdir}/LICENSE" "claude-desktop/LICENSE.nivora"
|
install-license "${srcdir}/LICENSE" "claude-desktop/LICENSE.nivora"
|
||||||
@@ -382,7 +134,5 @@ files() {
|
|||||||
files-find-license
|
files-find-license
|
||||||
files-find \
|
files-find \
|
||||||
"/usr/lib/claude-desktop/**/*" \
|
"/usr/lib/claude-desktop/**/*" \
|
||||||
"/usr/lib/claude-alt/**/*" \
|
"/usr/share/icons/hicolor/*/apps/claude-desktop.png"
|
||||||
"/usr/share/icons/hicolor/*/apps/claude-desktop.png" \
|
|
||||||
"/usr/share/icons/hicolor/*/apps/claude-alt.png"
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Инварианты
|
## Инварианты
|
||||||
|
|
||||||
- В репозитории ровно 19 каталогов с `Staplerfile`.
|
- В репозитории ровно 20 каталогов с `Staplerfile`.
|
||||||
- Каталог совпадает с `name` и командой в README.
|
- Каталог совпадает с `name` и командой в README.
|
||||||
- Upstream-версия не меняется из-за патча рецепта; для этого повышается `release`.
|
- Upstream-версия не меняется из-за патча рецепта; для этого повышается `release`.
|
||||||
- Desktop-id, AppStream component-id, units и пути данных не меняются без отдельной миграции.
|
- Desktop-id, AppStream component-id, units и пути данных не меняются без отдельной миграции.
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# ClaudeAlt
|
||||||
|
|
||||||
|
Самостоятельная вторая установка Claude, не входящая в пакет
|
||||||
|
`claude-desktop`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo stplr install nivora/claude-alt
|
||||||
|
```
|
||||||
|
|
||||||
|
Использует собственные runtime, профиль и desktop-идентичность. Иконки Nivora
|
||||||
|
переносятся без изменений. `claude-desktop` и `claude-alt` можно устанавливать,
|
||||||
|
обновлять и удалять независимо друг от друга.
|
||||||
@@ -2,10 +2,12 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
launcher="${repo_root}/claude-desktop/claude-alt"
|
launcher="${repo_root}/claude-alt/claude-alt"
|
||||||
icon="${repo_root}/claude-desktop/claude-alt.png"
|
icon="${repo_root}/claude-desktop/claude-alt.png"
|
||||||
primary_tray_icon="${repo_root}/claude-desktop/claude-tray-orange.png"
|
primary_tray_icon="${repo_root}/claude-desktop/claude-tray-orange.png"
|
||||||
alternate_tray_icon="${repo_root}/claude-desktop/claude-alt-tray-turquoise.png"
|
alternate_tray_icon="${repo_root}/claude-desktop/claude-alt-tray-turquoise.png"
|
||||||
|
primary_recipe="${repo_root}/claude-desktop/Staplerfile"
|
||||||
|
alternate_recipe="${repo_root}/claude-alt/Staplerfile"
|
||||||
temp_dir="$(mktemp -d)"
|
temp_dir="$(mktemp -d)"
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
@@ -14,6 +16,15 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if grep -Eq '/usr/lib/claude-alt|com\\.anthropic\\.ClaudeAlt|claude-alt\\.png' \
|
||||||
|
"$primary_recipe"; then
|
||||||
|
echo 'claude-desktop must not ship ClaudeAlt files' >&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"
|
mkdir -p "${temp_dir}/bin" "${temp_dir}/home"
|
||||||
log="${temp_dir}/launch.log"
|
log="${temp_dir}/launch.log"
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ readonly -a PACKAGES=(
|
|||||||
balena-etcher
|
balena-etcher
|
||||||
chatbox
|
chatbox
|
||||||
clash-verge-rev
|
clash-verge-rev
|
||||||
|
claude-alt
|
||||||
claude-desktop
|
claude-desktop
|
||||||
codex
|
codex
|
||||||
fisher
|
fisher
|
||||||
@@ -218,7 +219,7 @@ latest_version() {
|
|||||||
balena-etcher) github_latest_release balena-io/etcher ;;
|
balena-etcher) github_latest_release balena-io/etcher ;;
|
||||||
chatbox) latest_chatbox ;;
|
chatbox) latest_chatbox ;;
|
||||||
clash-verge-rev) github_latest_release clash-verge-rev/clash-verge-rev ;;
|
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 ;;
|
codex) github_latest_release Boria138/codex-app-linux ;;
|
||||||
fisher) github_latest_release jorgebucaran/fisher ;;
|
fisher) github_latest_release jorgebucaran/fisher ;;
|
||||||
github-desktop)
|
github-desktop)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ EXPECTED_PACKAGES = (
|
|||||||
"balena-etcher",
|
"balena-etcher",
|
||||||
"chatbox",
|
"chatbox",
|
||||||
"clash-verge-rev",
|
"clash-verge-rev",
|
||||||
|
"claude-alt",
|
||||||
"claude-desktop",
|
"claude-desktop",
|
||||||
"codex",
|
"codex",
|
||||||
"fisher",
|
"fisher",
|
||||||
@@ -43,6 +44,7 @@ REQUIRED_ROOT_FILES = {
|
|||||||
Path("docs/maintenance.md"),
|
Path("docs/maintenance.md"),
|
||||||
Path("docs/security-model.md"),
|
Path("docs/security-model.md"),
|
||||||
Path("docs/packages/claude-desktop.md"),
|
Path("docs/packages/claude-desktop.md"),
|
||||||
|
Path("docs/packages/claude-alt.md"),
|
||||||
Path("docs/packages/codex.md"),
|
Path("docs/packages/codex.md"),
|
||||||
Path("docs/packages/github-desktop.md"),
|
Path("docs/packages/github-desktop.md"),
|
||||||
Path("docs/packages/opencode.md"),
|
Path("docs/packages/opencode.md"),
|
||||||
|
|||||||
Reference in New Issue
Block a user