- M0: libghostty SSH 终端(渲染/输入/连接)+ 白屏修复(OutputGate) + 会话状态机/自动重连 - M1: tsnet 用户态组网 + SSH-over-tsnet(fd 桥),shell 级真机验证;R5(Go+gvisor+C+++Swift 同进程) retire - M1.5: tmux -CC 原生 tab(MVP) - 结构: packages/(TXCore·TXTransport), apps/TerminalX, vendor/(libghostty-spm/libssh2/mbedtls/tsnet-bridge), artifacts/ - 文档: CLAUDE.md + docs/HANDOFF.md(新会话入口) - 环境: 认证代理→依赖 vendor 本地化;Go 在 ~/.local/go;仅模拟器/未签名 - 待续: M2 mosh, tmux 多 pane, M4 安全(host key/SE) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
126 lines
4.4 KiB
Bash
Executable File
126 lines
4.4 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")/.."
|
|
if [ ! -f .root ]; then
|
|
echo "[*] malformed project structure"
|
|
exit 1
|
|
fi
|
|
|
|
INPUT_PATH=${1:-}
|
|
|
|
if [ -z "$INPUT_PATH" ]; then
|
|
echo "Usage: $0 <xcframework_or_zip>"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -e "$INPUT_PATH" ]; then
|
|
echo "[!] not found: $INPUT_PATH"
|
|
exit 1
|
|
fi
|
|
|
|
TEMP_DIR=
|
|
XCFRAMEWORK_PATH="$INPUT_PATH"
|
|
|
|
cleanup() {
|
|
if [ -n "$TEMP_DIR" ]; then
|
|
rm -rf "$TEMP_DIR"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
if [[ "$INPUT_PATH" == *.zip ]]; then
|
|
TEMP_DIR=$(mktemp -d)
|
|
ditto -x -k "$INPUT_PATH" "$TEMP_DIR"
|
|
XCFRAMEWORK_PATH=$(find "$TEMP_DIR" -maxdepth 1 -name "*.xcframework" -type d | head -1)
|
|
fi
|
|
|
|
if [ -z "$XCFRAMEWORK_PATH" ] || [ ! -d "$XCFRAMEWORK_PATH" ]; then
|
|
echo "[!] xcframework not found in input: $INPUT_PATH"
|
|
exit 1
|
|
fi
|
|
|
|
python3 - "$XCFRAMEWORK_PATH" <<'PY'
|
|
import os
|
|
import plistlib
|
|
import subprocess
|
|
import sys
|
|
|
|
xcframework = sys.argv[1]
|
|
info_path = os.path.join(xcframework, "Info.plist")
|
|
|
|
if not os.path.isfile(info_path):
|
|
raise SystemExit(f"[!] missing xcframework Info.plist: {info_path}")
|
|
|
|
for root, dirs, _ in os.walk(xcframework):
|
|
for directory in dirs:
|
|
if directory.endswith(".framework"):
|
|
raise SystemExit(f"[!] framework wrapper is not allowed in static-library xcframework: {os.path.join(root, directory)}")
|
|
|
|
with open(info_path, "rb") as handle:
|
|
info = plistlib.load(handle)
|
|
|
|
libraries = info.get("AvailableLibraries")
|
|
if not isinstance(libraries, list) or not libraries:
|
|
raise SystemExit("[!] AvailableLibraries is empty or missing")
|
|
|
|
for library in libraries:
|
|
identifier = library.get("LibraryIdentifier")
|
|
library_path = library.get("LibraryPath")
|
|
binary_path = library.get("BinaryPath")
|
|
headers_path = library.get("HeadersPath")
|
|
platform = library.get("SupportedPlatform")
|
|
platform_variant = library.get("SupportedPlatformVariant")
|
|
declared_architectures = set(library.get("SupportedArchitectures", []))
|
|
|
|
if not identifier:
|
|
raise SystemExit("[!] library entry missing LibraryIdentifier")
|
|
if platform not in {"ios", "macos", "tvos", "watchos", "xros"}:
|
|
raise SystemExit(f"[!] unsupported platform in {identifier}: {platform}")
|
|
if library_path != "libghostty.a" or binary_path != "libghostty.a":
|
|
raise SystemExit(f"[!] {identifier} must reference libghostty.a, got LibraryPath={library_path!r} BinaryPath={binary_path!r}")
|
|
if headers_path != "Headers":
|
|
raise SystemExit(f"[!] {identifier} must use HeadersPath=Headers, got {headers_path!r}")
|
|
|
|
variant_dir = os.path.join(xcframework, identifier)
|
|
archive_path = os.path.join(variant_dir, library_path)
|
|
headers_dir = os.path.join(variant_dir, headers_path)
|
|
header_path = os.path.join(headers_dir, "ghostty.h")
|
|
modulemap_path = os.path.join(headers_dir, "module.modulemap")
|
|
|
|
for required_path in (archive_path, header_path, modulemap_path):
|
|
if not os.path.exists(required_path):
|
|
raise SystemExit(f"[!] {identifier} missing required file: {required_path}")
|
|
|
|
actual_architectures = set(
|
|
subprocess.check_output(["lipo", "-archs", archive_path], text=True).split()
|
|
)
|
|
if actual_architectures != declared_architectures:
|
|
raise SystemExit(
|
|
f"[!] {identifier} architecture metadata mismatch: "
|
|
f"plist={sorted(declared_architectures)} archive={sorted(actual_architectures)}"
|
|
)
|
|
|
|
expected_architectures = {
|
|
("macos", None): {"arm64", "arm64e", "x86_64"},
|
|
("ios", None): {"arm64", "arm64e"},
|
|
("ios", "maccatalyst"): {"arm64", "arm64e", "x86_64"},
|
|
("ios", "simulator"): {"arm64", "x86_64"},
|
|
}.get((platform, platform_variant))
|
|
if expected_architectures is not None and actual_architectures != expected_architectures:
|
|
raise SystemExit(
|
|
f"[!] {identifier} architectures must be {sorted(expected_architectures)}, "
|
|
f"got {sorted(actual_architectures)}"
|
|
)
|
|
|
|
with open(modulemap_path, "r", encoding="utf-8") as handle:
|
|
modulemap = handle.read()
|
|
if "framework module libghostty" in modulemap:
|
|
raise SystemExit(f"[!] {identifier} module map must not declare a framework module")
|
|
if "module libghostty" not in modulemap:
|
|
raise SystemExit(f"[!] {identifier} module map does not declare module libghostty")
|
|
|
|
print(f"[*] verified static-library xcframework layout and architectures: {xcframework}")
|
|
PY
|