Files
2026-07-13 13:12:33 +08:00

1836 lines
60 KiB
Python

#!/usr/bin/env python3
"""Build a platform-local OpenSquilla wheelhouse release zip.
The output is intentionally not a source checkout and not a macOS DMG. It is a
zip containing the OpenSquilla wheel, dependency wheels for the current
platform/Python, install scripts, a manifest, and operator-facing README.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import platform
import shutil
import stat
import subprocess
import sys
import tarfile
import time
import tomllib
import urllib.parse
import urllib.request
import zipfile
from dataclasses import dataclass
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
DEFAULT_RUNTIME_RELEASE = "20260414"
DEFAULT_RUNTIME_PYTHON_VERSION = "3.12.13"
PYTHON_BUILD_STANDALONE_REPO = "astral-sh/python-build-standalone"
LFS_POINTER_LINE = "version https://git-lfs.github.com/spec/v1"
RELEASE_NOTICE_RELS = ("LICENSE", "THIRD_PARTY_NOTICES.md")
WHEEL_ROUTER_PREFIX = "opensquilla/squilla_router/models"
ROUTER_PROVENANCE_WHEEL_PATH = (
f"{WHEEL_ROUTER_PREFIX}/v4.2_phase3_inference/PROVENANCE.md"
)
TOKENJUICE_PROVENANCE_WHEEL_PATH = "opensquilla/plugins/tokenjuice/PROVENANCE.md"
ALLOWED_SKILL_REFERENCE_WHEEL_PATHS = frozenset(
{
"opensquilla/skills/bundled/html-coder/references/add-css-style.md",
"opensquilla/skills/bundled/html-coder/references/add-javascript.md",
"opensquilla/skills/bundled/html-coder/references/attributes.md",
"opensquilla/skills/bundled/html-coder/references/essentials.md",
"opensquilla/skills/bundled/html-coder/references/global-attributes.md",
"opensquilla/skills/bundled/html-coder/references/glossary.md",
"opensquilla/skills/bundled/html-coder/references/standards.md",
"opensquilla/skills/bundled/pptx/references/pptxgenjs.md",
"opensquilla/skills/bundled/pptx/references/python_pptx.md",
}
)
ROUTER_ASSET_RELS = (
"v4.2_phase3_inference/PROVENANCE.md",
"v4.2_phase3_inference/artifact_manifest.json",
"v4.2_phase3_inference/lgbm_main.bin",
"v4.2_phase3_inference/router.runtime.yaml",
"v4.2_phase3_inference/mlp/model.onnx",
"v4.2_phase3_inference/features/tfidf.pkl",
"v4.2_phase3_inference/bge_onnx/model.onnx",
)
REQUIRED_RUNTIME_MODULE_RELS = (
"opensquilla/cli/main.py",
"opensquilla/cli/dist_cmd.py",
"opensquilla/dist/__init__.py",
"opensquilla/dist/workspace_state.py",
)
FORBIDDEN_RELEASE_SEGMENTS = {".git", ".github", ".omx"}
FORBIDDEN_RELEASE_ROOTS = {"docs", "tests", "scripts"}
FORBIDDEN_RELEASE_TEXT_MARKERS = (
"INTERNAL_ORG_NAME",
"github.com/internal-org/opensquilla",
".internal/evidence",
"INTERNAL_RELEASE_NOTE.md",
"LOCAL_AGENT_NOTES.md",
)
TEXT_RELEASE_SUFFIXES = {
"",
".cfg",
".ini",
".json",
".md",
".py",
".text",
".toml",
".txt",
".yaml",
".yml",
}
@dataclass(frozen=True)
class RouterAssetCheck:
missing_files: tuple[Path, ...]
pointer_files: tuple[Path, ...]
@property
def ok(self) -> bool:
return not self.missing_files and not self.pointer_files
def run(args: list[str], *, cwd: Path, env: dict[str, str] | None = None) -> None:
print("+", " ".join(args), flush=True)
subprocess.run(args, cwd=cwd, check=True, env=env)
def read_project_version(repo_root: Path) -> str:
pyproject = tomllib.loads((repo_root / "pyproject.toml").read_text(encoding="utf-8"))
return str(pyproject["project"]["version"])
def repo_root_from_script() -> Path:
return Path(__file__).resolve().parents[1]
def build_subprocess_env(work_dir: Path, *, repo_root: Path | None = None) -> dict[str, str]:
env = os.environ.copy()
cache_root = work_dir
if repo_root is not None:
resolved_repo_root = repo_root.resolve()
cache_root = resolved_repo_root.parent / f".{resolved_repo_root.name}-wheelhouse-cache"
uv_cache = cache_root / "uv-cache"
pip_cache = cache_root / "pip-cache"
uv_cache.mkdir(parents=True, exist_ok=True)
pip_cache.mkdir(parents=True, exist_ok=True)
env["UV_CACHE_DIR"] = str(uv_cache)
env["PIP_CACHE_DIR"] = str(pip_cache)
return env
def copy_release_notices(release_root: Path, repo_root: Path | None = None) -> None:
root = repo_root or repo_root_from_script()
for rel in RELEASE_NOTICE_RELS:
source = root / rel
if not source.is_file():
raise SystemExit(f"Required release notice file is missing: {source}")
shutil.copy2(source, release_root / rel)
def platform_tag() -> str:
system = platform.system().lower() or "unknown"
machine = platform.machine().lower() or "unknown"
aliases = {
"darwin": "macos",
"amd64": "x64",
"x86_64": "x64",
"aarch64": "arm64",
}
return f"{aliases.get(system, system)}-{aliases.get(machine, machine)}"
def release_name(
*,
app_version: str,
platform_tag: str,
python_major: int,
python_minor: int,
profile: str,
portable: bool,
) -> str:
kind = "portable" if portable else "wheelhouse"
return (
f"OpenSquilla-{app_version}-{platform_tag}-"
f"py{python_major}{python_minor}-{profile}-{kind}"
)
def python_runtime_target_triple(platform_tag: str) -> str:
triples = {
"linux-arm64": "aarch64-unknown-linux-gnu",
"linux-x64": "x86_64-unknown-linux-gnu",
"macos-arm64": "aarch64-apple-darwin",
"macos-x64": "x86_64-apple-darwin",
"windows-arm64": "aarch64-pc-windows-msvc",
"windows-x64": "x86_64-pc-windows-msvc",
}
try:
return triples[platform_tag]
except KeyError as exc:
raise SystemExit(f"No bundled Python runtime mapping for platform: {platform_tag}") from exc
def python_runtime_asset_name(
*, python_version: str, runtime_release: str, platform_tag: str
) -> str:
triple = python_runtime_target_triple(platform_tag)
return f"cpython-{python_version}+{runtime_release}-{triple}-install_only_stripped.tar.gz"
def python_runtime_asset_url(asset_name: str, runtime_release: str) -> str:
quoted = urllib.parse.quote(asset_name, safe="")
return (
"https://github.com/"
f"{PYTHON_BUILD_STANDALONE_REPO}/releases/download/{runtime_release}/{quoted}"
)
def required_router_assets(model_root: Path) -> tuple[Path, ...]:
return tuple(model_root / rel for rel in ROUTER_ASSET_RELS)
def check_router_assets(model_root: Path) -> RouterAssetCheck:
missing_files: list[Path] = []
pointer_files: list[Path] = []
for path in required_router_assets(model_root):
if not path.is_file():
missing_files.append(path)
continue
first_line = path.read_text(encoding="utf-8", errors="ignore").splitlines()
if first_line and first_line[0].strip() == LFS_POINTER_LINE:
pointer_files.append(path)
return RouterAssetCheck(tuple(missing_files), tuple(pointer_files))
def _release_name(path: str) -> str:
name = path.replace("\\", "/")
while name.startswith("./"):
name = name[2:]
return name
def _contains_forbidden_release_segment(path: str) -> bool:
return any(part in FORBIDDEN_RELEASE_SEGMENTS for part in _release_name(path).split("/"))
def _is_allowed_runtime_markdown(path: str) -> bool:
name = _release_name(path)
if name == ROUTER_PROVENANCE_WHEEL_PATH:
return True
if name == TOKENJUICE_PROVENANCE_WHEEL_PATH:
return True
if name in ALLOWED_SKILL_REFERENCE_WHEEL_PATHS:
return True
if name.startswith("opensquilla/skills/bundled/") and name.endswith("/SKILL.md"):
return True
return name.startswith("opensquilla/identity/templates/bootstrap/") and name.endswith(".md")
def forbidden_release_wheel_entries(names: list[str] | tuple[str, ...]) -> list[str]:
violations: list[str] = []
for raw_name in names:
name = _release_name(raw_name)
if not name or name.endswith("/"):
continue
root = name.split("/", 1)[0]
if (
root in FORBIDDEN_RELEASE_ROOTS
or _contains_forbidden_release_segment(name)
or (name.endswith(".md") and not _is_allowed_runtime_markdown(name))
):
violations.append(name)
return violations
def forbidden_release_wheel_paths(wheel_path: Path) -> list[str]:
with ZipFile(wheel_path) as archive:
return forbidden_release_wheel_entries(tuple(archive.namelist()))
def forbidden_release_text_hits(wheel_path: Path) -> list[str]:
hits: list[str] = []
with ZipFile(wheel_path) as archive:
for info in archive.infolist():
name = _release_name(info.filename)
if not name or name.endswith("/"):
continue
suffix = Path(name).suffix.lower()
basename = Path(name).name
if suffix not in TEXT_RELEASE_SUFFIXES and basename != "METADATA":
continue
text = archive.read(info).decode("utf-8", errors="ignore")
for marker in FORBIDDEN_RELEASE_TEXT_MARKERS:
if marker in text:
hits.append(f"{name}: {marker}")
return hits
def missing_router_assets_in_wheel(wheel_path: Path) -> list[str]:
expected = {f"{WHEEL_ROUTER_PREFIX}/{rel}" for rel in ROUTER_ASSET_RELS}
with ZipFile(wheel_path) as archive:
names = set(archive.namelist())
return sorted(expected - names)
def missing_required_runtime_modules_in_wheel(wheel_path: Path) -> list[str]:
with ZipFile(wheel_path) as archive:
names = set(archive.namelist())
return sorted(set(REQUIRED_RUNTIME_MODULE_RELS) - names)
def find_built_wheel(wheel_dir: Path) -> Path:
wheels = sorted(wheel_dir.glob("opensquilla-*.whl"))
if len(wheels) != 1:
raise SystemExit(f"Expected one OpenSquilla wheel in {wheel_dir}, found {len(wheels)}")
return wheels[0]
def build_wheel(repo_root: Path, wheel_dir: Path, env: dict[str, str]) -> Path:
args = ["uv", "build", "--wheel", "--out-dir", str(wheel_dir), "--clear"]
try:
run(args, cwd=repo_root, env=env)
except subprocess.CalledProcessError as exc:
print(
f"uv build failed with exit code {exc.returncode}; retrying once.",
file=sys.stderr,
flush=True,
)
time.sleep(2)
run(args, cwd=repo_root, env=env)
return find_built_wheel(wheel_dir)
def pip_command(python_major: int, python_minor: int, *args: str) -> list[str]:
if sys.version_info[:2] != (python_major, python_minor):
raise SystemExit(
"Wheelhouse Python mismatch: "
f"running {sys.version_info.major}.{sys.version_info.minor}, "
f"target is {python_major}.{python_minor}."
)
return [
"uv",
"run",
"--python",
sys.executable,
"--with",
"pip",
"python",
"-m",
"pip",
*args,
]
def build_wheelhouse_command(
package_dir: Path,
wheel_path: Path,
profile: str,
*,
target_platform_tag: str,
python_major: int,
python_minor: int,
) -> list[str]:
validate_wheelhouse_target_platform(target_platform_tag)
extras = () if profile == "core" else (profile,)
target = str(wheel_path if not extras else f"{wheel_path}[{','.join(extras)}]")
return pip_command(
python_major,
python_minor,
"wheel",
"--wheel-dir",
str(package_dir),
target,
)
def validate_wheelhouse_target_platform(target_platform_tag: str) -> None:
host_platform_tag = platform_tag()
if target_platform_tag == host_platform_tag:
return
raise SystemExit(
"Wheelhouse builds must run on the target platform so dependency markers "
f"resolve correctly: host={host_platform_tag}, target={target_platform_tag}. "
"Use --skip-wheelhouse only for metadata/package-layout checks."
)
def download_wheelhouse(
package_dir: Path,
wheel_path: Path,
profile: str,
env: dict[str, str],
*,
target_platform_tag: str,
python_major: int,
python_minor: int,
) -> None:
validate_wheelhouse_target_platform(target_platform_tag)
run(
build_wheelhouse_command(
package_dir,
wheel_path,
profile,
target_platform_tag=target_platform_tag,
python_major=python_major,
python_minor=python_minor,
),
cwd=wheel_path.parent,
env=env,
)
def download_python_runtime_archive(
*,
download_dir: Path,
python_version: str,
runtime_release: str,
platform_tag: str,
) -> tuple[Path, str]:
asset_name = python_runtime_asset_name(
python_version=python_version,
runtime_release=runtime_release,
platform_tag=platform_tag,
)
archive_path = download_dir / asset_name
if archive_path.exists():
return archive_path, asset_name
download_dir.mkdir(parents=True, exist_ok=True)
url = python_runtime_asset_url(asset_name, runtime_release)
print(f"+ download {url}", flush=True)
with urllib.request.urlopen(url, timeout=120) as response:
archive_path.write_bytes(response.read())
return archive_path, asset_name
def _safe_extract_tar(archive: tarfile.TarFile, destination: Path) -> None:
destination_resolved = destination.resolve()
for member in archive.getmembers():
target = (destination / member.name).resolve()
if target != destination_resolved and destination_resolved not in target.parents:
raise SystemExit(f"Refusing unsafe runtime archive member: {member.name}")
archive.extractall(destination, filter="data")
def extract_python_runtime_archive(archive_path: Path, runtime_root: Path) -> None:
if runtime_root.exists():
shutil.rmtree(runtime_root)
extract_dir = runtime_root.parent / f"{runtime_root.name}.extract"
if extract_dir.exists():
shutil.rmtree(extract_dir)
extract_dir.mkdir(parents=True)
try:
with tarfile.open(archive_path, "r:gz") as archive:
_safe_extract_tar(archive, extract_dir)
candidate = extract_dir / "python"
if not candidate.is_dir():
child_dirs = [path for path in extract_dir.iterdir() if path.is_dir()]
if len(child_dirs) != 1:
raise SystemExit(f"Could not locate Python runtime root in {archive_path}")
candidate = child_dirs[0]
shutil.copytree(candidate, runtime_root)
finally:
shutil.rmtree(extract_dir, ignore_errors=True)
def prune_portable_runtime(runtime_root: Path) -> None:
site_packages = runtime_root / "Lib" / "site-packages"
if site_packages.is_dir():
removable_names = {
"_distutils_hack",
"pip",
"pkg_resources",
"setuptools",
"wheel",
}
removable_globs = (
"pip-*.dist-info",
"setuptools-*.dist-info",
"wheel-*.dist-info",
)
for name in removable_names:
path = site_packages / name
if path.is_dir():
shutil.rmtree(path)
elif path.is_file():
path.unlink()
for pattern in removable_globs:
for path in site_packages.glob(pattern):
if path.is_dir():
shutil.rmtree(path)
elif path.is_file():
path.unlink()
for pycache in sorted(runtime_root.rglob("__pycache__"), reverse=True):
if pycache.is_dir():
shutil.rmtree(pycache)
for pyc in runtime_root.rglob("*.pyc"):
if pyc.is_file():
pyc.unlink()
def install_portable_wheelhouse(release_root: Path) -> None:
"""Preinstall wheelhouse contents into the bundled Python runtime.
Portable zips should start like an app, not like a package manager. Avoid
runtime venv/ensurepip/pip work on user machines; Windows PowerShell and
antivirus hooks can make that path look hung even when the wheels are local.
"""
package_dir = release_root / "packages"
site_packages = release_root / "runtime" / "python" / "Lib" / "site-packages"
if not package_dir.is_dir() or not site_packages.is_dir():
raise SystemExit("Portable wheelhouse preinstall requires packages and site-packages.")
for wheel_path in sorted(package_dir.glob("*.whl")):
with zipfile.ZipFile(wheel_path) as wheel:
for info in wheel.infolist():
name = info.filename
if not name or name.endswith("/"):
continue
target_rel: Path | None
if ".data/" in name:
prefix, data_rel = name.split(".data/", 1)
_ = prefix
kind, _, remainder = data_rel.partition("/")
if kind in {"purelib", "platlib"} and remainder:
target_rel = Path(remainder)
else:
continue
else:
target_rel = Path(name)
target = site_packages / target_rel
target.parent.mkdir(parents=True, exist_ok=True)
with wheel.open(info) as src, target.open("wb") as dst:
shutil.copyfileobj(src, dst)
def render_install_sh(
*,
wheel_name: str,
profile: str,
python_major: int,
python_minor: int,
) -> str:
wheel_target = f"${{PACKAGE_DIR}}/{wheel_name}"
if profile != "core":
wheel_target = f"{wheel_target}[{profile}]"
return f"""#!/bin/sh
if [ -z "${{BASH_VERSION:-}}" ]; then
exec /usr/bin/env bash "$0" "$@"
fi
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
PACKAGE_DIR="${{SCRIPT_DIR}}/packages"
REQUIRED_PYTHON_MAJOR={python_major}
REQUIRED_PYTHON_MINOR={python_minor}
find_python() {{
local candidate
for candidate in "python${{REQUIRED_PYTHON_MAJOR}}.${{REQUIRED_PYTHON_MINOR}}" python3 python; do
if ! command -v "${{candidate}}" >/dev/null 2>&1; then
continue
fi
if "${{candidate}}" - <<PY >/dev/null 2>&1
import sys
expected = (${{REQUIRED_PYTHON_MAJOR}}, ${{REQUIRED_PYTHON_MINOR}})
raise SystemExit(0 if sys.version_info[:2] == expected else 1)
PY
then
command -v "${{candidate}}"
return 0
fi
done
return 1
}}
resolve_opensquilla_bin() {{
if command -v opensquilla >/dev/null 2>&1; then
command -v opensquilla
return 0
fi
if [[ -x "${{HOME}}/.local/bin/opensquilla" ]]; then
printf '%s\\n' "${{HOME}}/.local/bin/opensquilla"
return 0
fi
local user_python_bin
user_python_bin="${{HOME}}/Library/Python/${{REQUIRED_PYTHON_MAJOR}}.${{REQUIRED_PYTHON_MINOR}}/bin/opensquilla"
if [[ -x "${{user_python_bin}}" ]]; then
printf '%s\\n' "${{user_python_bin}}"
return 0
fi
return 1
}}
if [[ ! -d "${{PACKAGE_DIR}}" ]]; then
echo "OpenSquilla package directory not found: ${{PACKAGE_DIR}}" >&2
exit 1
fi
PYTHON_BIN="$(find_python || true)"
if [[ -z "${{PYTHON_BIN}}" ]]; then
echo "Python ${{REQUIRED_PYTHON_MAJOR}}.${{REQUIRED_PYTHON_MINOR}} is required." >&2
echo "Install it, then rerun: bash install.sh" >&2
exit 1
fi
echo "Installing OpenSquilla from local wheelhouse..."
if command -v uv >/dev/null 2>&1; then
uv tool install \\
--python "${{PYTHON_BIN}}" \\
--reinstall \\
--no-index \\
--find-links "${{PACKAGE_DIR}}" \\
"{wheel_target}"
else
"${{PYTHON_BIN}}" -m pip install \\
--user \\
--no-index \\
--find-links "${{PACKAGE_DIR}}" \\
"{wheel_target}"
fi
OPENSQUILLA_BIN="$(resolve_opensquilla_bin || true)"
if [[ -z "${{OPENSQUILLA_BIN}}" ]]; then
echo "OpenSquilla installed, but the executable was not found on PATH." >&2
echo "Add ~/.local/bin or your Python user scripts directory to PATH, then run opensquilla." >&2
exit 1
fi
"${{OPENSQUILLA_BIN}}" onboard --if-needed
echo
echo "OpenSquilla is installed."
echo "Start it with:"
echo " opensquilla gateway run"
echo
echo "Then open:"
echo " http://127.0.0.1:18791/control/"
"""
def render_install_ps1(
*,
wheel_name: str,
profile: str,
python_major: int,
python_minor: int,
) -> str:
wheel_target = f"$PackageDir\\{wheel_name}"
if profile != "core":
wheel_target = f"{wheel_target}[{profile}]"
python_version = f"{python_major}.{python_minor}"
return f"""$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$PackageDir = Join-Path $ScriptDir 'packages'
$RequiredPythonMajor = {python_major}
$RequiredPythonMinor = {python_minor}
function Find-Python {{
$candidates = @("py -{python_version}", "python")
foreach ($candidate in $candidates) {{
$parts = $candidate.Split(" ")
$exe = $parts[0]
$rest = @()
if ($parts.Length -gt 1) {{ $rest = $parts[1..($parts.Length - 1)] }}
if (-not (Get-Command $exe -ErrorAction SilentlyContinue)) {{ continue }}
$check = "import sys; expected = ($RequiredPythonMajor, $RequiredPythonMinor); " +
"raise SystemExit(0 if sys.version_info[:2] == expected else 1)"
& $exe @rest -c $check *> $null
if ($LASTEXITCODE -eq 0) {{ return @($exe) + $rest }}
}}
return $null
}}
function Resolve-OpenSquilla {{
$cmd = Get-Command opensquilla -ErrorAction SilentlyContinue
if ($cmd) {{ return $cmd.Source }}
$scriptDir = Join-Path $env:APPDATA "Python\\Python{python_major}{python_minor}\\Scripts"
$script = Join-Path $scriptDir "opensquilla.exe"
if (Test-Path $script) {{ return $script }}
return $null
}}
if (-not (Test-Path $PackageDir)) {{
throw "OpenSquilla package directory not found: $PackageDir"
}}
$Python = Find-Python
if (-not $Python) {{
throw "Python {python_version} is required. Install it, then rerun .\\install.ps1."
}}
Write-Host "Installing OpenSquilla from local wheelhouse..."
if (Get-Command uv -ErrorAction SilentlyContinue) {{
& uv tool install `
--python "{python_version}" `
--reinstall `
--no-index `
--find-links $PackageDir `
"{wheel_target}"
}} else {{
$PythonExe = $Python[0]
$PythonArgs = @()
if ($Python.Length -gt 1) {{
$PythonArgs = $Python[1..($Python.Length - 1)]
}}
& $PythonExe @PythonArgs -m pip install `
--user `
--no-index `
--find-links $PackageDir `
"{wheel_target}"
}}
if ($LASTEXITCODE -ne 0) {{
throw "OpenSquilla installation failed with exit code $LASTEXITCODE."
}}
$OpenSquillaBin = Resolve-OpenSquilla
if (-not $OpenSquillaBin) {{
throw "OpenSquilla installed, but the executable was not found on PATH."
}}
& $OpenSquillaBin onboard --if-needed
if ($LASTEXITCODE -ne 0) {{
throw "OpenSquilla onboarding failed with exit code $LASTEXITCODE."
}}
Write-Host ""
Write-Host "OpenSquilla is installed."
Write-Host "Start it with:"
Write-Host " opensquilla gateway run"
Write-Host ""
Write-Host "Then open:"
Write-Host " http://127.0.0.1:18791/control/"
"""
def _install_target(base: str, profile: str) -> str:
extras = () if profile == "core" else (profile,)
return base if not extras else f"{base}[{','.join(extras)}]"
def render_start_sh(profile: str = "recommended") -> str:
target = _install_target("opensquilla", profile)
script = """#!/bin/sh
if [ -z "${BASH_VERSION:-}" ]; then
exec /usr/bin/env bash "$0" "$@"
fi
set -euo pipefail
CLI_MODE=0
if [[ "${1:-}" == "--cli" ]]; then
CLI_MODE=1
shift
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PACKAGE_DIR="${SCRIPT_DIR}/packages"
PYTHON_BIN="${SCRIPT_DIR}/runtime/python/bin/python3"
if [[ -z "${OPENSQUILLA_LLM_API_KEY:-}" && -n "${OPENROUTER_API_KEY:-}" ]]; then
export OPENSQUILLA_LLM_API_KEY="${OPENROUTER_API_KEY}"
fi
if [[ ! -x "${PYTHON_BIN}" ]]; then
echo "Bundled Python runtime not found: ${PYTHON_BIN}" >&2
exit 1
fi
if [[ ! -d "${PACKAGE_DIR}" ]]; then
echo "OpenSquilla package directory not found: ${PACKAGE_DIR}" >&2
exit 1
fi
OPENSQUILLA_WHEEL="$(
find "${PACKAGE_DIR}" -maxdepth 1 -type f -name 'opensquilla-*.whl' |
sort |
head -n 1
)"
if [[ -z "${OPENSQUILLA_WHEEL}" ]]; then
echo "OpenSquilla wheel not found in ${PACKAGE_DIR}" >&2
exit 1
fi
WHEEL_HASH="$(
"${PYTHON_BIN}" -c '
import hashlib, pathlib, sys
print(hashlib.sha256(pathlib.Path(sys.argv[1]).read_bytes()).hexdigest()[:12])
' "${OPENSQUILLA_WHEEL}"
)"
VENV_DIR="${SCRIPT_DIR}/.venv-${WHEEL_HASH}"
VENV_PYTHON="${VENV_DIR}/bin/python"
INSTALL_MARKER="${VENV_DIR}/.opensquilla-wheelhouse-${WHEEL_HASH}"
export PATH="${SCRIPT_DIR}:${VENV_DIR}/bin:${SCRIPT_DIR}/runtime/python/bin:${PATH}"
RELEASE_ID="$(
"${PYTHON_BIN}" -c '
import hashlib, sys
print(hashlib.sha256(f"{sys.argv[1]}|{sys.argv[2]}".encode("utf-8")).hexdigest()[:12])
' "${SCRIPT_DIR}" "${WHEEL_HASH}"
)"
DATA_BASE="${XDG_DATA_HOME:-${HOME}/.local/share}"
PORTABLE_DATA_DIR="${OPENSQUILLA_PORTABLE_HOME:-${DATA_BASE}/OpenSquilla/portable/${RELEASE_ID}}"
if [[ -z "${OPENSQUILLA_GATEWAY_CONFIG_PATH:-}" ]]; then
export OPENSQUILLA_GATEWAY_CONFIG_PATH="${PORTABLE_DATA_DIR}/config.toml"
fi
if [[ -z "${OPENSQUILLA_STATE_DIR:-}" ]]; then
export OPENSQUILLA_STATE_DIR="${PORTABLE_DATA_DIR}"
fi
if [[ -z "${OPENSQUILLA_GATEWAY_STATE_DIR:-}" ]]; then
export OPENSQUILLA_GATEWAY_STATE_DIR="${OPENSQUILLA_STATE_DIR}/state"
fi
if [[ -z "${OPENSQUILLA_GATEWAY_WORKSPACE_DIR:-}" ]]; then
export OPENSQUILLA_GATEWAY_WORKSPACE_DIR="${OPENSQUILLA_STATE_DIR}/workspace"
fi
mkdir -p "${OPENSQUILLA_STATE_DIR}"
if [[ ! -x "${VENV_PYTHON}" ]]; then
echo "Creating local OpenSquilla environment..."
"${PYTHON_BIN}" -m venv --without-pip "${VENV_DIR}"
fi
if [[ ! -f "${INSTALL_MARKER}" ]]; then
echo "Installing OpenSquilla from bundled wheels..."
SITE_PACKAGES="$("${VENV_PYTHON}" -c 'import site; print(site.getsitepackages()[0])')"
"${PYTHON_BIN}" - "${PACKAGE_DIR}" "${SITE_PACKAGES}" <<'PY'
import pathlib
import shutil
import sys
import zipfile
package_dir = pathlib.Path(sys.argv[1])
site_packages = pathlib.Path(sys.argv[2])
site_packages.mkdir(parents=True, exist_ok=True)
for wheel_path in sorted(package_dir.glob("*.whl")):
with zipfile.ZipFile(wheel_path) as wheel:
for info in wheel.infolist():
name = info.filename
if not name or name.endswith("/"):
continue
if ".data/" in name:
_prefix, data_rel = name.split(".data/", 1)
kind, _sep, remainder = data_rel.partition("/")
if kind not in {"purelib", "platlib"} or not remainder:
continue
target_rel = pathlib.Path(remainder)
else:
target_rel = pathlib.Path(name)
target = site_packages / target_rel
target.parent.mkdir(parents=True, exist_ok=True)
with wheel.open(info) as src, target.open("wb") as dst:
shutil.copyfileobj(src, dst)
PY
touch "${INSTALL_MARKER}"
fi
OPENSQUILLA_BIN="${VENV_PYTHON}"
OPENSQUILLA_MODULE=( "-m" "opensquilla.cli.main" )
if [[ "${CLI_MODE}" == "1" ]]; then
exec "${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" "$@"
fi
if [[ ! -f "${OPENSQUILLA_GATEWAY_CONFIG_PATH}" && -n "${OPENROUTER_API_KEY:-}" ]]; then
"${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" onboard \\
--provider openrouter \\
--api-key-env OPENROUTER_API_KEY \\
--minimal
else
"${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" onboard
fi
echo
echo "Starting OpenSquilla gateway."
echo "Web UI: http://127.0.0.1:18791/control/"
echo "Press Ctrl+C in this terminal to stop the gateway."
if [[ -t 1 ]]; then
exec "${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" gateway run
else
LOG_DIR="${OPENSQUILLA_STATE_DIR}/logs"
mkdir -p "${LOG_DIR}"
CONSOLE_LOG="${OPENSQUILLA_STATE_DIR}/logs/gateway-console.log"
echo "Console log: ${CONSOLE_LOG}"
"${OPENSQUILLA_BIN}" "${OPENSQUILLA_MODULE[@]}" gateway run 2>&1 | tee -a "${CONSOLE_LOG}"
exit "${PIPESTATUS[0]}"
fi
"""
return script.replace("__TARGET__", target)
def render_start_ps1(profile: str = "recommended") -> str:
target = _install_target("opensquilla", profile)
requires_router_runtime = "$true" if profile == "recommended" else "$false"
script = """param(
[switch]$Cli,
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$CliArgs
)
$ErrorActionPreference = "Stop"
$env:PYTHONUTF8 = '1'
$env:PYTHONIOENCODING = 'utf-8:replace'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$PackageDir = Join-Path $ScriptDir 'packages'
$PythonBin = Join-Path $ScriptDir 'runtime\\python\\python.exe'
$VenvBase = if ($env:LOCALAPPDATA) { $env:LOCALAPPDATA } else { $env:TEMP }
$VenvRoot = Join-Path $VenvBase 'OpenSquilla\\venvs'
$RequiresRouterRuntime = __REQUIRES_ROUTER_RUNTIME__
if ((-not $env:OPENSQUILLA_LLM_API_KEY) -and $env:OPENROUTER_API_KEY) {
$env:OPENSQUILLA_LLM_API_KEY = $env:OPENROUTER_API_KEY
}
if (-not (Test-Path $PythonBin)) {
throw "Bundled Python runtime not found: $PythonBin"
}
if (-not (Test-Path $PackageDir)) {
throw "OpenSquilla package directory not found: $PackageDir"
}
function Test-WindowsVCRedistInstalled {
if (-not [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform(
[System.Runtime.InteropServices.OSPlatform]::Windows
)) {
return $true
}
$runtimeKeys = @(
'HKLM:\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64',
'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64'
)
foreach ($key in $runtimeKeys) {
if (-not (Test-Path $key)) {
continue
}
$runtime = Get-ItemProperty -Path $key -ErrorAction SilentlyContinue
if ($runtime -and $runtime.Installed -eq 1 -and $runtime.Major -ge 14) {
return $true
}
}
return $false
}
function Test-WindowsAdmin {
if (-not [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform(
[System.Runtime.InteropServices.OSPlatform]::Windows
)) {
return $true
}
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Get-WindowsVCRedistInstaller {
$redistUrl = 'https://aka.ms/vs/17/release/vc_redist.x64.exe'
$candidateInstallers = @(
(Join-Path $ScriptDir 'vc_redist.x64.exe'),
(Join-Path $ScriptDir 'redist\\vc_redist.x64.exe'),
(Join-Path $ScriptDir 'runtime\\vc_redist.x64.exe')
)
$installerPath = $candidateInstallers |
Where-Object { Test-Path -LiteralPath $_ } |
Select-Object -First 1
if ($installerPath) {
return $installerPath
}
$downloadDir = Join-Path ([System.IO.Path]::GetTempPath()) 'OpenSquilla'
$installerPath = Join-Path $downloadDir 'vc_redist.x64.exe'
New-Item -ItemType Directory -Path $downloadDir -Force | Out-Null
Write-Host (
'OpenSquilla: downloading Microsoft Visual C++ Redistributable ' +
'2015-2022 x64 from Microsoft.'
)
try {
Invoke-WebRequest -Uri $redistUrl -OutFile $installerPath -UseBasicParsing
return $installerPath
} catch {
Write-Warning (
'OpenSquilla: could not download Microsoft Visual C++ ' +
"Redistributable from $redistUrl. Error: $($_.Exception.Message)"
)
return $null
}
}
function Install-WindowsVCRedistWithInstaller {
param(
[switch]$Repair
)
$installerPath = Get-WindowsVCRedistInstaller
if (-not $installerPath) {
return $false
}
$action = if ($Repair) { 'repairing' } else { 'installing' }
Write-Host (
"OpenSquilla: $action Microsoft Visual C++ Redistributable 2015-2022 x64..."
)
$redistArgs = if ($Repair) {
@('/repair', '/quiet', '/norestart')
} else {
@('/install', '/quiet', '/norestart')
}
try {
if (Test-WindowsAdmin) {
$process = Start-Process -FilePath $installerPath `
-ArgumentList $redistArgs `
-Wait `
-PassThru
} else {
Write-Host (
'OpenSquilla: administrator approval may be requested to ' +
'install or repair Microsoft Visual C++ Redistributable.'
)
$process = Start-Process -FilePath $installerPath `
-ArgumentList $redistArgs `
-Verb RunAs `
-Wait `
-PassThru
}
} catch {
Write-Warning (
'OpenSquilla: Visual C++ Redistributable installer could not be ' +
"started. Error: $($_.Exception.Message)"
)
return $false
}
if ($process.ExitCode -in @(0, 1638, 3010)) {
Write-Host 'OpenSquilla: Microsoft Visual C++ Redistributable is ready.'
if ($process.ExitCode -eq 3010) {
Write-Warning (
'OpenSquilla: the Visual C++ installer requested a reboot; ' +
'restart Windows if ONNX Runtime still fails to load.'
)
}
return $true
}
Write-Warning (
'OpenSquilla: Microsoft Visual C++ Redistributable installer exited ' +
"with code $($process.ExitCode)."
)
return $false
}
function Install-WindowsVCRedistIfNeeded {
param(
[switch]$Repair
)
if (-not $RequiresRouterRuntime) {
return $true
}
if ($env:OPENSQUILLA_SKIP_VC_REDIST -eq '1') {
Write-Host (
'OpenSquilla: skipping Microsoft Visual C++ Redistributable check ' +
'because OPENSQUILLA_SKIP_VC_REDIST=1.'
)
return $true
}
if ((Test-WindowsVCRedistInstalled) -and -not $Repair) {
return $true
}
$redistUrl = 'https://aka.ms/vs/17/release/vc_redist.x64.exe'
if ($Repair) {
if (Install-WindowsVCRedistWithInstaller -Repair) {
return $true
}
} elseif (Install-WindowsVCRedistWithInstaller) {
return $true
}
$winget = if ($Repair) { $null } else { Get-Command winget -ErrorAction SilentlyContinue }
if ($winget) {
Write-Host (
'OpenSquilla: Microsoft Visual C++ Redistributable not detected; ' +
'installing with winget.'
)
$wingetArgs = @(
'install',
'--id',
'Microsoft.VCRedist.2015+.x64',
'--exact',
'--silent',
'--accept-package-agreements',
'--accept-source-agreements'
)
& winget @wingetArgs
if ($LASTEXITCODE -eq 0) {
Write-Host 'OpenSquilla: Microsoft Visual C++ Redistributable installation completed.'
return $true
}
Write-Warning (
'OpenSquilla: winget could not install Microsoft Visual C++ ' +
"Redistributable (exit $LASTEXITCODE)."
)
}
Write-Warning (
'OpenSquilla: Microsoft Visual C++ Redistributable 2015-2022 x64 is ' +
'required for the bundled ONNX router.'
)
Write-Warning (
'OpenSquilla can still start with safe router fallback, but bundled ' +
'ONNX model routing is disabled until this runtime is installed.'
)
Write-Warning (
"If automatic installation fails, install it manually: $redistUrl"
)
Write-Warning (
'After installing, reopen PowerShell and restart OpenSquilla.'
)
return $false
}
function Test-OnnxRuntimeImport {
if (-not (Test-Path $VenvPython)) {
return $false
}
& $VenvPython -c "import onnxruntime as ort; print('onnxruntime', ort.__version__)" | Out-Host
return ($LASTEXITCODE -eq 0)
}
function Repair-WindowsVCRedistForOnnxIfNeeded {
if (-not $RequiresRouterRuntime) {
return
}
if ($env:OPENSQUILLA_SKIP_VC_REDIST -eq '1') {
return
}
if (Test-OnnxRuntimeImport) {
return
}
Write-Warning (
'OpenSquilla: ONNX Runtime failed to import after setup. Attempting ' +
'Visual C++ Redistributable repair before starting the gateway.'
)
Install-WindowsVCRedistIfNeeded -Repair | Out-Null
if (Test-OnnxRuntimeImport) {
return
}
Write-Warning (
'OpenSquilla: ONNX Runtime still failed after Visual C++ repair. If ' +
'the router warning remains, check CPU/VM AVX compatibility or install ' +
'the Microsoft Visual C++ Redistributable manually: ' +
'https://aka.ms/vs/17/release/vc_redist.x64.exe'
)
}
if (-not (Test-Path $VenvRoot)) {
New-Item -ItemType Directory -Path $VenvRoot -Force | Out-Null
}
$OpenSquillaWheel = Get-ChildItem -Path $PackageDir -Filter 'opensquilla-*.whl' |
Sort-Object Name |
Select-Object -First 1
if (-not $OpenSquillaWheel) {
throw "OpenSquilla wheel not found in $PackageDir"
}
$Sha256 = [System.Security.Cryptography.SHA256]::Create()
$WheelStream = [System.IO.File]::OpenRead($OpenSquillaWheel.FullName)
try {
$WheelHashFull = -join ($Sha256.ComputeHash($WheelStream) | ForEach-Object {
$_.ToString('x2')
})
} finally {
$WheelStream.Dispose()
$Sha256.Dispose()
}
$WheelHash = $WheelHashFull.Substring(0, 12).ToLowerInvariant()
$Hash = [System.Security.Cryptography.SHA256]::Create().ComputeHash(
[System.Text.Encoding]::UTF8.GetBytes("$ScriptDir|$WheelHash")
)
$ReleaseId = -join ($Hash[0..5] | ForEach-Object { $_.ToString('x2') })
$VenvDir = Join-Path $VenvRoot $ReleaseId
$VenvPython = Join-Path $VenvDir 'Scripts\\python.exe'
$InstallMarker = Join-Path $VenvDir ".opensquilla-wheelhouse-$WheelHash"
$env:PATH = "$ScriptDir;$ScriptDir\\runtime\\python;$env:PATH"
$env:PATH = "$VenvDir\\Scripts;$env:PATH"
$PortableDataDir = if ($env:OPENSQUILLA_PORTABLE_HOME) {
$env:OPENSQUILLA_PORTABLE_HOME
} else {
Join-Path $VenvBase "OpenSquilla\\portable\\$ReleaseId"
}
if (-not $env:OPENSQUILLA_GATEWAY_CONFIG_PATH) {
$env:OPENSQUILLA_GATEWAY_CONFIG_PATH = Join-Path $PortableDataDir 'config.toml'
}
if (-not $env:OPENSQUILLA_STATE_DIR) {
$env:OPENSQUILLA_STATE_DIR = $PortableDataDir
}
if (-not $env:OPENSQUILLA_GATEWAY_STATE_DIR) {
$env:OPENSQUILLA_GATEWAY_STATE_DIR = Join-Path $env:OPENSQUILLA_STATE_DIR 'state'
}
if (-not $env:OPENSQUILLA_GATEWAY_WORKSPACE_DIR) {
$env:OPENSQUILLA_GATEWAY_WORKSPACE_DIR = Join-Path $env:OPENSQUILLA_STATE_DIR 'workspace'
}
New-Item -ItemType Directory -Path $env:OPENSQUILLA_STATE_DIR -Force | Out-Null
Install-WindowsVCRedistIfNeeded | Out-Null
if (-not (Test-Path $VenvPython)) {
Write-Host "Creating local OpenSquilla environment..."
& $PythonBin -m venv --without-pip $VenvDir
if ($LASTEXITCODE -ne 0) {
throw "OpenSquilla environment creation failed with exit code $LASTEXITCODE."
}
}
if (-not (Test-Path $InstallMarker)) {
Write-Host "Installing OpenSquilla from bundled wheels..."
$SitePackages = & $VenvPython -c "import site; print(site.getsitepackages()[0])"
if ($LASTEXITCODE -ne 0) {
throw "OpenSquilla site-packages lookup failed with exit code $LASTEXITCODE."
}
$WheelInstallScript = @'
import pathlib
import shutil
import sys
import zipfile
package_dir = pathlib.Path(sys.argv[1])
site_packages = pathlib.Path(sys.argv[2])
site_packages.mkdir(parents=True, exist_ok=True)
for wheel_path in sorted(package_dir.glob("*.whl")):
with zipfile.ZipFile(wheel_path) as wheel:
for info in wheel.infolist():
name = info.filename
if not name or name.endswith("/"):
continue
if ".data/" in name:
_prefix, data_rel = name.split(".data/", 1)
kind, _sep, remainder = data_rel.partition("/")
if kind not in {"purelib", "platlib"} or not remainder:
continue
target_rel = pathlib.Path(remainder)
else:
target_rel = pathlib.Path(name)
target = site_packages / target_rel
target.parent.mkdir(parents=True, exist_ok=True)
with wheel.open(info) as src, target.open("wb") as dst:
shutil.copyfileobj(src, dst)
'@
$WheelInstallScript | & $PythonBin - $PackageDir $SitePackages
if ($LASTEXITCODE -ne 0) {
throw "OpenSquilla bundled wheel installation failed with exit code $LASTEXITCODE."
}
New-Item -ItemType File -Path $InstallMarker -Force | Out-Null
}
Repair-WindowsVCRedistForOnnxIfNeeded
$OpenSquillaArgs = @("-m", "opensquilla.cli.main")
if ($Cli) {
& $VenvPython @OpenSquillaArgs @CliArgs
exit $LASTEXITCODE
}
if ((-not (Test-Path $env:OPENSQUILLA_GATEWAY_CONFIG_PATH)) -and $env:OPENROUTER_API_KEY) {
& $VenvPython @OpenSquillaArgs onboard `
--provider openrouter `
--api-key-env OPENROUTER_API_KEY `
--minimal
} else {
& $VenvPython @OpenSquillaArgs onboard
}
if ($LASTEXITCODE -ne 0) {
throw "OpenSquilla onboarding failed with exit code $LASTEXITCODE."
}
Write-Host ""
Write-Host "Starting OpenSquilla gateway."
Write-Host "Web UI: http://127.0.0.1:18791/control/"
Write-Host "Press Ctrl+C in this terminal to stop the gateway."
$OutputRedirected = [Console]::IsOutputRedirected
if (-not $OutputRedirected) {
& $VenvPython @OpenSquillaArgs gateway run
$GatewayExitCode = $LASTEXITCODE
} else {
$LogDir = Join-Path $env:OPENSQUILLA_STATE_DIR 'logs'
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
$ConsoleLog = Join-Path $LogDir 'gateway-console.log'
Write-Host "Console log: $ConsoleLog"
$PreviousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = "Continue"
try {
& $VenvPython @OpenSquillaArgs gateway run 2>&1 |
ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
$_.ToString()
} else {
$_
}
} |
Tee-Object -FilePath $ConsoleLog -Append
$GatewayExitCode = $LASTEXITCODE
} finally {
$ErrorActionPreference = $PreviousErrorActionPreference
}
}
exit $GatewayExitCode
"""
return script.replace("__TARGET__", target).replace(
"__REQUIRES_ROUTER_RUNTIME__", requires_router_runtime
)
def render_cli_sh() -> str:
return """#!/bin/sh
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
exec "${SCRIPT_DIR}/start.sh" --cli "$@"
"""
def render_cli_cmd() -> str:
return (
"@echo off\r\n"
'cd /d "%~dp0"\r\n'
'set "OSQ_POWERSHELL=powershell.exe"\r\n'
'where pwsh.exe >nul 2>nul && set "OSQ_POWERSHELL=pwsh.exe"\r\n'
'"%OSQ_POWERSHELL%" -NoLogo -NoProfile -ExecutionPolicy Bypass '
'-File "%~dp0start.ps1" -Cli %*\r\n'
)
def render_shell_cmd() -> str:
return (
"@echo off\r\n"
"title OpenSquilla Shell\r\n"
'cd /d "%~dp0"\r\n'
'set "OSQ_POWERSHELL=powershell.exe"\r\n'
'where pwsh.exe >nul 2>nul && set "OSQ_POWERSHELL=pwsh.exe"\r\n'
'"%OSQ_POWERSHELL%" -NoLogo -NoExit -NoProfile -ExecutionPolicy Bypass -Command '
"\"Set-Location -LiteralPath '%~dp0'; "
"function global:opensquilla { & (Join-Path (Get-Location) 'opensquilla.cmd') @args }; "
"Write-Host 'OpenSquilla portable shell'; "
"Write-Host 'Run commands like:'; "
"Write-Host ' opensquilla onboard --provider openrouter'; "
"Write-Host ' opensquilla gateway run'\"\r\n"
)
def render_start_cmd() -> str:
return (
"@echo off\r\n"
"title OpenSquilla Gateway\r\n"
'cd /d "%~dp0"\r\n'
'set "OSQ_POWERSHELL=powershell.exe"\r\n'
'where pwsh.exe >nul 2>nul && set "OSQ_POWERSHELL=pwsh.exe"\r\n'
'"%OSQ_POWERSHELL%" -NoLogo -NoExit -NoProfile -ExecutionPolicy Bypass '
'-File "%~dp0start.ps1"\r\n'
)
def render_readme(
*,
app_version: str,
profile: str,
platform_tag: str,
python_major: int,
python_minor: int,
portable: bool,
) -> str:
windows_target = platform_tag.startswith("windows-")
if portable:
release_kind = "Portable Release"
unix_commands = "bash start.sh"
windows_command = ".\\start.ps1"
python_note = "Python is bundled in this zip."
setup_note = (
"Config, workspace, logs, memory, and runtime state use the normal "
"user-level OpenSquilla directory."
)
else:
release_kind = "Wheelhouse Release"
unix_commands = "bash install.sh\nopensquilla gateway run"
windows_command = ".\\install.ps1\nopensquilla gateway run"
python_note = f"Requires Python {python_major}.{python_minor}."
setup_note = (
"The installer runs idempotent onboarding after installation. To "
"reconfigure later, run `opensquilla onboard` for the full wizard or "
"`opensquilla configure <section>` for one area."
)
if windows_target:
if portable:
command_section = f"""## Windows
1. Right-click `Start OpenSquilla.cmd` -> **Run as administrator**.
2. Complete onboarding.
3. Open `http://127.0.0.1:18791/control/`.
Notes:
- Keep the terminal open. Closing it stops the gateway.
- OpenSquilla 0.1.0 preview builds are unsigned. The supported portable launch
path is administrator launch.
- If SmartScreen appears, choose **More info** -> **Run anyway**.
- If Smart App Control or enterprise policy blocks the unsigned app, use the
`uv tool install` wheel path instead.
- Microsoft documents that SmartScreen checks downloaded apps and Smart App
Control can block unknown, unsigned code.
<details>
<summary>Advanced portable usage</summary>
Use these options only when you want scripted setup or portable CLI commands.
To start from PowerShell:
```powershell
Set-ExecutionPolicy -Scope Process Bypass
{windows_command}
```
If `OPENROUTER_API_KEY` is present and no local config exists, the launcher
writes an OpenRouter env-reference config and starts the gateway without asking
you to paste the key.
The portable package does not install a global `opensquilla` command. For a
terminal where `opensquilla ...` commands work, run
`OpenSquilla Shell.cmd`, or run commands from this folder:
```powershell
.\\opensquilla.cmd onboard
```
</details>
"""
else:
command_section = f"""## Windows PowerShell
```powershell
Set-ExecutionPolicy -Scope Process Bypass
{windows_command}
```
"""
else:
if portable:
command_section = f"""## macOS / Linux
1. Run the launcher from the extracted folder:
```sh
{unix_commands}
```
2. Keep the terminal open. Closing the terminal stops the gateway.
3. Complete onboarding. On first run, choose a provider and paste the requested
keys; later starts let you review or change the config.
4. Open `http://127.0.0.1:18791/control/`.
"""
else:
command_section = f"""## macOS / Linux
```sh
{unix_commands}
```
"""
web_ui_note = (
""
if portable
else "Open `http://127.0.0.1:18791/control/`.\n\n"
)
return f"""# OpenSquilla {app_version} {release_kind}
Build target:
- platform: `{platform_tag}`
- Python: `{python_major}.{python_minor}`
- profile: `{profile}`
{command_section.rstrip()}
{web_ui_note}{python_note}
{setup_note}
"""
def write_manifest(
path: Path,
*,
app_version: str,
profile: str,
platform_tag: str,
python_major: int,
python_minor: int,
wheel_name: str,
package_count: int,
include_router_assets: bool,
portable: bool,
runtime_release: str,
runtime_asset: str,
) -> None:
payload = {
"name": "OpenSquilla wheelhouse zip",
"version": app_version,
"profile": profile,
"platform_tag": platform_tag,
"python": f"{python_major}.{python_minor}",
"wheel_name": wheel_name,
"package_count": package_count,
"include_router_assets": include_router_assets,
"portable": portable,
"runtime_release": runtime_release,
"runtime_asset": runtime_asset,
}
path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
def prepare_release_tree(
release_root: Path,
wheel_path: Path,
*,
app_version: str,
profile: str,
platform_tag: str,
python_major: int,
python_minor: int,
include_router_assets: bool,
portable: bool,
runtime_release: str,
runtime_asset: str,
runtime_root: Path | None = None,
) -> Path:
if release_root.exists():
shutil.rmtree(release_root)
package_dir = release_root / "packages"
package_dir.mkdir(parents=True)
bundled_wheel = package_dir / wheel_path.name
shutil.copy2(wheel_path, bundled_wheel)
if portable:
if runtime_root is None or not runtime_root.is_dir():
raise SystemExit("Portable release requires a Python runtime directory.")
runtime_target = release_root / "runtime" / "python"
shutil.copytree(runtime_root, runtime_target)
prune_portable_runtime(runtime_target)
start_sh = release_root / "start.sh"
start_sh.write_text(render_start_sh(profile), encoding="utf-8")
start_sh.chmod(start_sh.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
cli_sh = release_root / "opensquilla"
cli_sh.write_text(render_cli_sh(), encoding="utf-8")
cli_sh.chmod(cli_sh.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
(release_root / "start.ps1").write_text(render_start_ps1(profile), encoding="utf-8")
if platform_tag.startswith("windows-"):
(release_root / "Start OpenSquilla.cmd").write_text(
render_start_cmd(),
encoding="utf-8",
newline="",
)
(release_root / "opensquilla.cmd").write_text(
render_cli_cmd(),
encoding="utf-8",
newline="",
)
(release_root / "OpenSquilla Shell.cmd").write_text(
render_shell_cmd(),
encoding="utf-8",
newline="",
)
else:
install_sh = release_root / "install.sh"
install_sh.write_text(
render_install_sh(
wheel_name=wheel_path.name,
profile=profile,
python_major=python_major,
python_minor=python_minor,
),
encoding="utf-8",
)
install_sh.chmod(install_sh.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
(release_root / "install.ps1").write_text(
render_install_ps1(
wheel_name=wheel_path.name,
profile=profile,
python_major=python_major,
python_minor=python_minor,
),
encoding="utf-8",
)
(release_root / "README.md").write_text(
render_readme(
app_version=app_version,
profile=profile,
platform_tag=platform_tag,
python_major=python_major,
python_minor=python_minor,
portable=portable,
),
encoding="utf-8",
)
copy_release_notices(release_root)
write_manifest(
release_root / "manifest.json",
app_version=app_version,
profile=profile,
platform_tag=platform_tag,
python_major=python_major,
python_minor=python_minor,
wheel_name=wheel_path.name,
package_count=len(list(package_dir.glob("*.whl"))),
include_router_assets=include_router_assets,
portable=portable,
runtime_release=runtime_release,
runtime_asset=runtime_asset,
)
return bundled_wheel
def create_zip(release_root: Path, zip_path: Path, *, archive_root: str | None = None) -> None:
zip_path.parent.mkdir(parents=True, exist_ok=True)
if zip_path.exists():
zip_path.unlink()
root_parent = release_root.parent
with ZipFile(zip_path, "w", ZIP_DEFLATED) as archive:
for path in sorted(p for p in release_root.rglob("*") if p.is_file()):
if archive_root:
rel = Path(archive_root, path.relative_to(release_root)).as_posix()
else:
rel = path.relative_to(root_parent).as_posix()
info = ZipInfo(rel)
info.compress_type = ZIP_DEFLATED
source_mode = stat.S_IMODE(path.stat().st_mode)
executable_by_path = rel.endswith(("/install.sh", "/start.sh")) or (
"/runtime/python/bin/" in rel
)
mode = (
0o755
if executable_by_path
or source_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
else 0o644
)
info.external_attr = mode << 16
archive.writestr(info, path.read_bytes())
def sha256_digest(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def write_sha256(path: Path) -> Path:
digest = sha256_digest(path)
checksum_path = path.with_suffix(path.suffix + ".sha256")
checksum_path.write_text(f"{digest} {path.name}\n", encoding="utf-8")
return checksum_path
def write_sha256s(paths: tuple[Path, ...] | list[Path], checksum_path: Path) -> Path:
lines = [f"{sha256_digest(path)} {path.name}" for path in sorted(paths, key=lambda p: p.name)]
checksum_path.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")
return checksum_path
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--profile", choices=("recommended", "core"), default="recommended")
parser.add_argument("--output-dir", type=Path, default=Path("dist"))
parser.add_argument("--work-dir", type=Path, default=Path("build/wheelhouse-zip"))
parser.add_argument(
"--bundle-python-runtime",
action="store_true",
help="Bundle a portable python-build-standalone runtime and start scripts.",
)
parser.add_argument(
"--platform-tag",
choices=(
"linux-arm64",
"linux-x64",
"macos-arm64",
"macos-x64",
"windows-arm64",
"windows-x64",
),
help="Target platform tag. Defaults to the current host platform.",
)
parser.add_argument(
"--python-runtime-release",
default=DEFAULT_RUNTIME_RELEASE,
help="python-build-standalone release tag to bundle.",
)
parser.add_argument(
"--python-runtime-version",
default=DEFAULT_RUNTIME_PYTHON_VERSION,
help="Full CPython runtime version from python-build-standalone.",
)
parser.add_argument(
"--python-runtime-archive",
type=Path,
help="Use a pre-downloaded python-build-standalone install_only archive.",
)
parser.add_argument(
"--skip-wheelhouse",
action="store_true",
help="Only place the OpenSquilla wheel in packages/ for script debugging.",
)
parser.add_argument(
"--skip-zip",
action="store_true",
help="Prepare the release directory without creating the zip.",
)
return parser
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
repo_root = Path(__file__).resolve().parents[1]
app_version = read_project_version(repo_root)
include_router_assets = args.profile == "recommended"
if include_router_assets:
model_root = repo_root / "src" / "opensquilla" / "squilla_router" / "models"
asset_check = check_router_assets(model_root)
if not asset_check.ok:
for path in asset_check.missing_files:
print(f"Missing router asset: {path}", file=sys.stderr)
for path in asset_check.pointer_files:
print(f"Git LFS pointer file is not hydrated: {path}", file=sys.stderr)
print(
'Run: git lfs pull --include="src/opensquilla/squilla_router/models/**"',
file=sys.stderr,
)
return 1
work_dir = (repo_root / args.work_dir).resolve()
env = build_subprocess_env(work_dir, repo_root=repo_root)
wheel_dir = work_dir / "wheels"
tag = args.platform_tag or platform_tag()
if not args.skip_wheelhouse:
validate_wheelhouse_target_platform(tag)
name = release_name(
app_version=app_version,
platform_tag=tag,
python_major=sys.version_info.major,
python_minor=sys.version_info.minor,
profile=args.profile,
portable=args.bundle_python_runtime,
)
release_root = work_dir / name
runtime_root: Path | None = None
runtime_asset = ""
if args.bundle_python_runtime:
if sys.version_info[:2] != (3, 12):
raise SystemExit("Portable release builds currently require Python 3.12.")
if args.python_runtime_archive:
runtime_archive = args.python_runtime_archive.resolve()
runtime_asset = runtime_archive.name
else:
runtime_archive, runtime_asset = download_python_runtime_archive(
download_dir=work_dir / "runtime-downloads",
python_version=args.python_runtime_version,
runtime_release=args.python_runtime_release,
platform_tag=tag,
)
runtime_root = work_dir / "runtime" / "python"
extract_python_runtime_archive(runtime_archive, runtime_root)
wheel_path = build_wheel(repo_root, wheel_dir, env)
missing_runtime_modules = missing_required_runtime_modules_in_wheel(wheel_path)
if missing_runtime_modules:
print("Built wheel is missing required runtime modules:", file=sys.stderr)
for entry in missing_runtime_modules:
print(f" {entry}", file=sys.stderr)
return 1
wheel_violations = forbidden_release_wheel_paths(wheel_path)
if wheel_violations:
print("Built wheel contains forbidden release entries:", file=sys.stderr)
for entry in wheel_violations:
print(f" {entry}", file=sys.stderr)
return 1
text_violations = forbidden_release_text_hits(wheel_path)
if text_violations:
print("Built wheel contains internal release text markers:", file=sys.stderr)
for entry in text_violations:
print(f" {entry}", file=sys.stderr)
return 1
if include_router_assets:
missing_from_wheel = missing_router_assets_in_wheel(wheel_path)
if missing_from_wheel:
print("Router assets are hydrated but missing from the built wheel:", file=sys.stderr)
for entry in missing_from_wheel:
print(f" {entry}", file=sys.stderr)
return 1
bundled_wheel = prepare_release_tree(
release_root,
wheel_path,
app_version=app_version,
profile=args.profile,
platform_tag=tag,
python_major=sys.version_info.major,
python_minor=sys.version_info.minor,
include_router_assets=include_router_assets,
portable=args.bundle_python_runtime,
runtime_release=args.python_runtime_release if args.bundle_python_runtime else "",
runtime_asset=runtime_asset,
runtime_root=runtime_root,
)
package_dir = bundled_wheel.parent
if not args.skip_wheelhouse:
download_wheelhouse(
package_dir,
bundled_wheel,
args.profile,
env,
target_platform_tag=tag,
python_major=sys.version_info.major,
python_minor=sys.version_info.minor,
)
write_manifest(
release_root / "manifest.json",
app_version=app_version,
profile=args.profile,
platform_tag=tag,
python_major=sys.version_info.major,
python_minor=sys.version_info.minor,
wheel_name=bundled_wheel.name,
package_count=len(list(package_dir.glob("*.whl"))),
include_router_assets=include_router_assets,
portable=args.bundle_python_runtime,
runtime_release=args.python_runtime_release if args.bundle_python_runtime else "",
runtime_asset=runtime_asset,
)
if args.skip_zip:
print(release_root)
return 0
zip_path = (repo_root / args.output_dir / f"{name}.zip").resolve()
archive_root = f"OpenSquilla-{app_version}" if args.bundle_python_runtime else None
create_zip(release_root, zip_path, archive_root=archive_root)
checksum_path = write_sha256(zip_path)
checksums_path = write_sha256s(
tuple(zip_path.parent.glob("*.zip")), zip_path.parent / "SHA256SUMS"
)
print(zip_path)
print(checksum_path)
print(checksums_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())