diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6bcd541..fd675ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,8 +69,14 @@ jobs: merge-multiple: true - name: Package SkillHub upload ZIP run: | - ./scripts/package-skill.sh version="${GITHUB_REF_NAME#v}" + install -D -m 0755 \ + "browser-cli-v${version}-aarch64-apple-darwin" \ + skills/lexmount-browser/bin/browser-cli + install -D -m 0755 \ + "browser-cli-v${version}-x86_64-pc-windows-msvc.exe" \ + skills/lexmount-browser/bin/browser-cli.exe + ./scripts/package-skill.sh cp dist/lexmount-browser.zip "lexmount-browser-v${version}-skillhub.zip" sha256sum "lexmount-browser-v${version}-skillhub.zip" > "lexmount-browser-v${version}-skillhub.zip.sha256" - run: cat *.sha256 | sort -k2 > SHA256SUMS diff --git a/.gitignore b/.gitignore index fbba973..241fe7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target/ /dist/ +/skills/lexmount-browser/bin/ *.tmp diff --git a/Cargo.lock b/Cargo.lock index 42dcf00..80881fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1208,7 +1208,7 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "lexmount-browser" -version = "1.1.7" +version = "1.1.8" dependencies = [ "base64 0.22.1", "clap", diff --git a/Cargo.toml b/Cargo.toml index b1ff033..1c4d1f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lexmount-browser" -version = "1.1.7" +version = "1.1.8" edition = "2024" license = "MIT" description = "Native Rust SDK and CLI for Lexmount cloud browsers" diff --git a/README.md b/README.md index 19797d3..68604e4 100644 --- a/README.md +++ b/README.md @@ -25,16 +25,22 @@ surface. The publishable Skill is in `skills/lexmount-browser`. Build a deterministic ZIP: ```bash +mkdir -p skills/lexmount-browser/bin +cp /path/to/macos-arm64/browser-cli skills/lexmount-browser/bin/browser-cli +cp /path/to/windows-x64/browser-cli.exe skills/lexmount-browser/bin/browser-cli.exe ./scripts/package-skill.sh ``` -The ZIP contains `SKILL.md` at its archive root and can be uploaded directly to -SkillHub. Tagged releases publish it as `lexmount-browser-v-skillhub.zip` -alongside the two platform binaries and `SHA256SUMS`. +The ZIP contains `SKILL.md` at its archive root plus the signed macOS arm64 +binary at `bin/browser-cli` and the Windows x64 binary at +`bin/browser-cli.exe`. It can be uploaded directly to SkillHub. Tagged releases +publish it as `lexmount-browser-v-skillhub.zip` alongside the standalone +platform binaries and `SHA256SUMS`. -The package installer downloads a pinned GitHub Release binary and verifies its -SHA-256 digest before installation. Set `LEXMOUNT_BROWSER_CLI_VERSION` only when -testing a different published release. +The Skill uses the bundled binary for the current platform. Its bootstrap script +downloads a pinned GitHub Release binary and verifies its SHA-256 digest only as +a fallback when the bundled binary is missing. Set `LEXMOUNT_BROWSER_CLI_VERSION` +only when testing a different published release. Published binaries are intentionally limited to two targets: macOS arm64 and Windows x64. The macOS binary is signed with a Developer ID Application diff --git a/scripts/package-skill.sh b/scripts/package-skill.sh index 91973f2..8d5ff00 100755 --- a/scripts/package-skill.sh +++ b/scripts/package-skill.sh @@ -8,9 +8,30 @@ trap 'rm -rf "$staging_dir"' EXIT INT TERM mkdir -p "$dist_dir" rm -f "$dist_dir/lexmount-browser.zip" +for required_binary in \ + "$skill_dir/bin/browser-cli" \ + "$skill_dir/bin/browser-cli.exe" +do + if [ ! -f "$required_binary" ]; then + echo "Missing required Skill binary: $required_binary" >&2 + exit 1 + fi +done + +file "$skill_dir/bin/browser-cli" | grep -q 'Mach-O 64-bit.*arm64' || { + echo "bin/browser-cli is not a macOS arm64 executable" >&2 + exit 1 +} +file "$skill_dir/bin/browser-cli.exe" | grep -q 'PE32+ executable.*x86-64' || { + echo "bin/browser-cli.exe is not a Windows x64 executable" >&2 + exit 1 +} + ( cd "$skill_dir" - find . -type f ! -path './bin/*' ! -name '.DS_Store' -print | LC_ALL=C sort | + find . -type f ! -name '.DS_Store' \ + \( ! -path './bin/*' -o -path './bin/browser-cli' -o -path './bin/browser-cli.exe' \) \ + -print | LC_ALL=C sort | while IFS= read -r relative_path; do mkdir -p "$staging_dir/$(dirname -- "$relative_path")" cp "$relative_path" "$staging_dir/$relative_path" @@ -20,6 +41,7 @@ rm -f "$dist_dir/lexmount-browser.zip" find "$staging_dir" -type d -exec chmod 0755 {} + find "$staging_dir" -type f -exec chmod 0644 {} + find "$staging_dir/scripts" -type f \( -name '*.sh' -o -name '*.ps1' \) -exec chmod 0755 {} + +find "$staging_dir/bin" -type f -exec chmod 0755 {} + find "$staging_dir" -exec touch -t 198001010000 {} + ( diff --git a/skills/lexmount-browser/SKILL.md b/skills/lexmount-browser/SKILL.md index 3a97cd6..277029e 100644 --- a/skills/lexmount-browser/SKILL.md +++ b/skills/lexmount-browser/SKILL.md @@ -5,11 +5,16 @@ description: Use Lexmount cloud browsers to open and interact with JavaScript-he # Lexmount Browser -Use the native Rust binary at `${CODEBUDDY_SKILL_DIR}/bin/browser-cli` on macOS or `${CODEBUDDY_SKILL_DIR}/bin/browser-cli.exe` on Windows. Release binaries support macOS arm64 and Windows x64 and emit JSON. The examples below abbreviate that path as `browser-cli`; resolve it to the platform path before running commands. +Use the bundled native Rust binary that matches the current platform: + +- macOS arm64: `${CODEBUDDY_SKILL_DIR}/bin/browser-cli` +- Windows x64: `${CODEBUDDY_SKILL_DIR}/bin/browser-cli.exe` + +Do not run the binary for the other platform. Both binaries emit JSON. The examples below abbreviate the selected platform path as `browser-cli`; resolve it before running commands. ## Setup -1. Run the platform `doctor` script from `${CODEBUDDY_SKILL_DIR}/scripts/`. If the binary is missing, run the matching bootstrap script after telling the user that it downloads a fixed-version release binary and verifies its SHA-256 digest. +1. Run `doctor.sh` on macOS or `doctor.ps1` on Windows from `${CODEBUDDY_SKILL_DIR}/scripts/`. Use the bundled platform binary. Only if that binary is missing, run the matching bootstrap script after telling the user that it downloads a fixed-version replacement and verifies its SHA-256 digest. 2. If credentials are missing, run `browser-cli auth login`. Let the user approve in their browser. Never ask them to paste an API key into chat. 3. Run `browser-cli doctor` again. Continue only when `ready_for_browser_actions` is true. diff --git a/skills/lexmount-browser/scripts/bootstrap.ps1 b/skills/lexmount-browser/scripts/bootstrap.ps1 index d32b71e..39ce165 100644 --- a/skills/lexmount-browser/scripts/bootstrap.ps1 +++ b/skills/lexmount-browser/scripts/bootstrap.ps1 @@ -1,6 +1,7 @@ $ErrorActionPreference = "Stop" -$version = if ($env:LEXMOUNT_BROWSER_CLI_VERSION) { $env:LEXMOUNT_BROWSER_CLI_VERSION } else { "1.1.7" } -if (-not [Environment]::Is64BitOperatingSystem) { throw "Only 64-bit Windows is supported" } +$version = if ($env:LEXMOUNT_BROWSER_CLI_VERSION) { $env:LEXMOUNT_BROWSER_CLI_VERSION } else { "1.1.8" } +$architecture = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } +if ($architecture -ne "AMD64") { throw "Only Windows x64 is supported" } $asset = "browser-cli-v$version-x86_64-pc-windows-msvc.exe" $repo = "https://github.com/lexmount/browser-cli-rs/releases/download/v$version" $tmp = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString()) diff --git a/skills/lexmount-browser/scripts/bootstrap.sh b/skills/lexmount-browser/scripts/bootstrap.sh index ac9348b..9a455cc 100755 --- a/skills/lexmount-browser/scripts/bootstrap.sh +++ b/skills/lexmount-browser/scripts/bootstrap.sh @@ -1,7 +1,7 @@ #!/bin/sh set -eu -version="${LEXMOUNT_BROWSER_CLI_VERSION:-1.1.7}" +version="${LEXMOUNT_BROWSER_CLI_VERSION:-1.1.8}" repo="https://github.com/lexmount/browser-cli-rs/releases/download/v${version}" case "$(uname -s)-$(uname -m)" in Darwin-arm64) target="aarch64-apple-darwin" ;; diff --git a/skills/lexmount-browser/scripts/doctor.ps1 b/skills/lexmount-browser/scripts/doctor.ps1 index 2e5c182..fc9f34a 100644 --- a/skills/lexmount-browser/scripts/doctor.ps1 +++ b/skills/lexmount-browser/scripts/doctor.ps1 @@ -1,4 +1,9 @@ $ErrorActionPreference = "Stop" +$architecture = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } +if ($architecture -ne "AMD64") { + Write-Output '{"ok":false,"error":"unsupported_platform","message":"This Skill supports Windows x64 through scripts/doctor.ps1 and macOS arm64 through scripts/doctor.sh."}' + exit 2 +} $skillBinary = Join-Path (Split-Path -Parent $PSScriptRoot) "bin\browser-cli.exe" if (Test-Path $skillBinary) { & $skillBinary doctor; exit $LASTEXITCODE } Write-Output '{"ok":false,"error":"command_not_found","message":"Skill-local browser-cli.exe is missing. Run scripts/bootstrap.ps1 first."}' diff --git a/skills/lexmount-browser/scripts/doctor.sh b/skills/lexmount-browser/scripts/doctor.sh index c634bf9..c0f8882 100755 --- a/skills/lexmount-browser/scripts/doctor.sh +++ b/skills/lexmount-browser/scripts/doctor.sh @@ -1,5 +1,9 @@ #!/bin/sh set -eu +case "$(uname -s)-$(uname -m)" in + Darwin-arm64) ;; + *) echo '{"ok":false,"error":"unsupported_platform","message":"This Skill supports macOS arm64 through scripts/doctor.sh and Windows x64 through scripts/doctor.ps1."}'; exit 2 ;; +esac skill_dir="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)" if [ -x "$skill_dir/bin/browser-cli" ]; then exec "$skill_dir/bin/browser-cli" doctor; fi echo '{"ok":false,"error":"command_not_found","message":"Skill-local browser-cli is missing. Run scripts/bootstrap.sh first."}'