From 1fe379afbcd6009a499c43d93ab6c046f2889151 Mon Sep 17 00:00:00 2001 From: Alex Hancock Date: Thu, 9 Apr 2026 11:03:04 -0400 Subject: [PATCH] fix: node publishing (#8438) --- ui/acp/package.json | 2 +- .../goose-binary-darwin-arm64/package.json | 2 +- .../goose-binary-darwin-x64/package.json | 2 +- .../goose-binary-linux-arm64/package.json | 2 +- .../goose-binary-linux-x64/package.json | 2 +- .../goose-binary-win32-x64/package.json | 2 +- ui/scripts/publish.sh | 255 +++++++++++++++--- ui/text/package.json | 2 +- 8 files changed, 219 insertions(+), 50 deletions(-) diff --git a/ui/acp/package.json b/ui/acp/package.json index eec8c94c..a8b77a84 100644 --- a/ui/acp/package.json +++ b/ui/acp/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose-acp", - "version": "0.1.2", + "version": "0.2.0", "description": "Agent Client Protocol (ACP) SDK for Goose AI agent", "license": "Apache-2.0", "repository": { diff --git a/ui/goose-binary/goose-binary-darwin-arm64/package.json b/ui/goose-binary/goose-binary-darwin-arm64/package.json index d95bcd06..9c02d98e 100644 --- a/ui/goose-binary/goose-binary-darwin-arm64/package.json +++ b/ui/goose-binary/goose-binary-darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose-binary-darwin-arm64", - "version": "0.1.2", + "version": "0.2.0", "description": "Goose binary for macOS ARM64", "license": "Apache-2.0", "repository": { diff --git a/ui/goose-binary/goose-binary-darwin-x64/package.json b/ui/goose-binary/goose-binary-darwin-x64/package.json index 450cc3e1..3f119bc9 100644 --- a/ui/goose-binary/goose-binary-darwin-x64/package.json +++ b/ui/goose-binary/goose-binary-darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose-binary-darwin-x64", - "version": "0.1.2", + "version": "0.2.0", "description": "Goose binary for macOS x64", "license": "Apache-2.0", "repository": { diff --git a/ui/goose-binary/goose-binary-linux-arm64/package.json b/ui/goose-binary/goose-binary-linux-arm64/package.json index 747c8b05..5fcac160 100644 --- a/ui/goose-binary/goose-binary-linux-arm64/package.json +++ b/ui/goose-binary/goose-binary-linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose-binary-linux-arm64", - "version": "0.1.2", + "version": "0.2.0", "description": "Goose binary for Linux ARM64", "license": "Apache-2.0", "repository": { diff --git a/ui/goose-binary/goose-binary-linux-x64/package.json b/ui/goose-binary/goose-binary-linux-x64/package.json index 1ea0d16b..d12e7428 100644 --- a/ui/goose-binary/goose-binary-linux-x64/package.json +++ b/ui/goose-binary/goose-binary-linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose-binary-linux-x64", - "version": "0.1.2", + "version": "0.2.0", "description": "Goose binary for Linux x64", "license": "Apache-2.0", "repository": { diff --git a/ui/goose-binary/goose-binary-win32-x64/package.json b/ui/goose-binary/goose-binary-win32-x64/package.json index 8de7dcb6..804348b3 100644 --- a/ui/goose-binary/goose-binary-win32-x64/package.json +++ b/ui/goose-binary/goose-binary-win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose-binary-win32-x64", - "version": "0.1.0", + "version": "0.2.0", "description": "Goose binary for Windows x64", "license": "Apache-2.0", "repository": { diff --git a/ui/scripts/publish.sh b/ui/scripts/publish.sh index f3d4ff96..f765a0dc 100755 --- a/ui/scripts/publish.sh +++ b/ui/scripts/publish.sh @@ -1,63 +1,232 @@ #!/usr/bin/env bash set -euo pipefail -# Publishes @aaif/goose-acp, @aaif/goose, and all native binary packages to npm. +# Builds and publishes all @aaif npm packages: +# @aaif/goose-acp — ACP TypeScript SDK +# @aaif/goose-binary-* — platform-specific goose CLI binaries +# @aaif/goose — TUI that depends on the above +# +# Linux binaries are built inside Docker containers on their native arch. +# macOS binaries are built natively (requires macOS host with Rust). +# Windows is skipped (no cross-compilation support yet). # # Usage: -# ./ui/scripts/publish.sh # publish all (dry-run) -# ./ui/scripts/publish.sh --real # publish for real +# ./ui/scripts/publish.sh # dry run +# ./ui/scripts/publish.sh --real # publish to npmjs.org # -# Prerequisites: -# - npm login to the @aaif scope -# - Native binaries built via npm run build:native:all in ui/acp +# Requires: +# - Docker with buildx (linux/amd64 + linux/arm64) +# - Rust toolchain with aarch64-apple-darwin and x86_64-apple-darwin targets +# - pnpm +# - NPM_PUBLISH_TOKEN env var (or ~/.npm-publish-token file) REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" -ACP_DIR="${REPO_ROOT}/ui/acp" NATIVE_DIR="${REPO_ROOT}/ui/goose-binary" +ACP_DIR="${REPO_ROOT}/ui/acp" TEXT_DIR="${REPO_ROOT}/ui/text" +REGISTRY="https://registry.npmjs.org" +DOCKER_IMAGE="rust:1.92-bookworm" -DRY_RUN="--dry-run" +DRY_RUN=true if [[ "${1:-}" == "--real" ]]; then - DRY_RUN="" - echo "==> Publishing for real" + DRY_RUN=false + echo "==> REAL publish to ${REGISTRY}" else - echo "==> Dry run (pass --real to publish)" + echo "==> Dry run (pass --real to publish for real)" fi -# Build and publish @aaif/goose-acp first (dependency of @aaif/goose) -echo "==> Building @aaif/goose-acp" -(cd "${ACP_DIR}" && npm run build) - -echo "==> Publishing @aaif/goose-acp" -(cd "${ACP_DIR}" && npm publish --access public ${DRY_RUN}) - -# Build @aaif/goose -echo "==> Building @aaif/goose" -(cd "${TEXT_DIR}" && npm run build) - -NATIVE_PACKAGES=( - "goose-binary-darwin-arm64" - "goose-binary-darwin-x64" - "goose-binary-linux-arm64" - "goose-binary-linux-x64" - "goose-binary-win32-x64" -) - -# Publish native binary packages -for pkg in "${NATIVE_PACKAGES[@]}"; do - pkg_dir="${NATIVE_DIR}/${pkg}" - - if [ ! -f "${pkg_dir}/bin/goose" ] && [ ! -f "${pkg_dir}/bin/goose.exe" ]; then - echo " SKIP ${pkg} (no binary found — run npm run build:native:all in ui/acp first)" - continue +# Resolve NPM token +if [[ -z "${NPM_PUBLISH_TOKEN:-}" ]]; then + TOKEN_FILE="${HOME}/.npm-publish-token" + if [[ -f "${TOKEN_FILE}" ]]; then + NPM_PUBLISH_TOKEN="$(cat "${TOKEN_FILE}")" + export NPM_PUBLISH_TOKEN + echo "==> Loaded NPM_PUBLISH_TOKEN from ${TOKEN_FILE}" + else + echo "ERROR: NPM_PUBLISH_TOKEN not set and ${TOKEN_FILE} not found" >&2 + exit 1 fi +fi - echo "==> Publishing @aaif/${pkg}" - (cd "${pkg_dir}" && npm publish --access public ${DRY_RUN}) +# --------------------------------------------------------------------------- +# Step 1: Build macOS binaries natively +# --------------------------------------------------------------------------- +build_macos() { + local platform="$1" target="$2" + local pkg_dir="${NATIVE_DIR}/goose-binary-${platform}/bin" + + echo "==> Building goose for ${platform} (${target}) natively" + cargo build --release --target "${target}" --bin goose --manifest-path "${REPO_ROOT}/Cargo.toml" + + mkdir -p "${pkg_dir}" + cp "${REPO_ROOT}/target/${target}/release/goose" "${pkg_dir}/goose" + chmod +x "${pkg_dir}/goose" + echo " ✅ ${pkg_dir}/goose" +} + +build_macos darwin-arm64 aarch64-apple-darwin +build_macos darwin-x64 x86_64-apple-darwin + +# --------------------------------------------------------------------------- +# Step 2: Build Linux binaries in Docker on native arch +# --------------------------------------------------------------------------- +DOCKER_BUILD_SCRIPT='#!/bin/bash +set -euo pipefail +apt-get update -qq +apt-get install -y -qq --no-install-recommends \ + build-essential cmake pkg-config libssl-dev libdbus-1-dev \ + libclang-dev protobuf-compiler libprotobuf-dev ca-certificates >/dev/null 2>&1 +echo "==> Compiling goose (this takes a while)..." +cargo build --release --bin goose +cp /build/target/release/goose /output/goose +chmod +x /output/goose +echo "==> Done" +' + +build_linux_docker() { + local platform="$1" docker_platform="$2" + local pkg_dir="${NATIVE_DIR}/goose-binary-${platform}/bin" + + echo "==> Building goose for ${platform} in Docker (${docker_platform})" + + mkdir -p "${pkg_dir}" + + # Create a temporary directory with only what cargo needs. + # This avoids sending multi-GB Docker contexts. + local ctx + ctx="$(mktemp -d)" + trap "rm -rf '${ctx}'" RETURN 2>/dev/null || true + + # Copy Rust source + rsync -a --delete \ + --exclude='target/' \ + --exclude='.git/' \ + --exclude='node_modules/' \ + --exclude='documentation/' \ + --exclude='ui/desktop/' \ + --exclude='ui/goose-binary/*/bin/' \ + --exclude='evals/' \ + --exclude='.hermit/' \ + --exclude='*.jsonl' \ + --exclude='bench_results/' \ + "${REPO_ROOT}/" "${ctx}/" + + # Write a minimal Dockerfile into the context + cat > "${ctx}/Dockerfile.npm-build" <<'DEOF' +FROM rust:1.92-bookworm +RUN apt-get update -qq && \ + apt-get install -y -qq --no-install-recommends \ + build-essential cmake pkg-config libssl-dev libdbus-1-dev \ + libclang-dev protobuf-compiler libprotobuf-dev ca-certificates >/dev/null 2>&1 && \ + rm -rf /var/lib/apt/lists/* +WORKDIR /build +COPY . . +RUN mkdir -p /output && \ + cargo build --release --bin goose && \ + cp target/release/goose /output/goose && \ + chmod +x /output/goose +DEOF + + # Build in Docker and extract the binary + local iid="goose-npm-build-${platform}-$$" + docker build \ + --platform "${docker_platform}" \ + -f "${ctx}/Dockerfile.npm-build" \ + -t "${iid}" \ + "${ctx}" + + # Extract binary from the image + local cid + cid="$(docker create --platform "${docker_platform}" "${iid}" /bin/true)" + docker cp "${cid}:/output/goose" "${pkg_dir}/goose" + docker rm "${cid}" >/dev/null + docker rmi "${iid}" >/dev/null 2>&1 || true + chmod +x "${pkg_dir}/goose" + + rm -rf "${ctx}" + + echo " ✅ ${pkg_dir}/goose" +} + +build_linux_docker linux-x64 linux/amd64 +build_linux_docker linux-arm64 linux/arm64 + +# --------------------------------------------------------------------------- +# Step 3: Verify all binaries +# --------------------------------------------------------------------------- +echo "" +echo "==> Verifying binaries" +for plat in darwin-arm64 darwin-x64 linux-arm64 linux-x64; do + bin="${NATIVE_DIR}/goose-binary-${plat}/bin/goose" + if [[ ! -f "${bin}" ]]; then + echo " ❌ MISSING: ${bin}" + exit 1 + fi + size=$(stat -f%z "${bin}" 2>/dev/null || stat -c%s "${bin}" 2>/dev/null) + size_mb=$(( size / 1048576 )) + filetype=$(file -b "${bin}" | head -c 60) + echo " ✅ ${plat}: ${size_mb} MB — ${filetype}" done -# Publish the main package -echo "==> Publishing @aaif/goose" -(cd "${TEXT_DIR}" && npm publish --access public ${DRY_RUN}) +# --------------------------------------------------------------------------- +# Step 4: Build TypeScript packages +# --------------------------------------------------------------------------- +echo "" +echo "==> Building @aaif/goose-acp" +(cd "${ACP_DIR}" && pnpm run build:ts) -echo "==> Done" +echo "==> Building @aaif/goose" +(cd "${TEXT_DIR}" && pnpm run build) + +# --------------------------------------------------------------------------- +# Step 5: Publish +# --------------------------------------------------------------------------- +echo "" + +PUBLISH_ARGS=(--access public --no-git-checks --registry "${REGISTRY}" --tag latest) +if [[ "${DRY_RUN}" == "true" ]]; then + PUBLISH_ARGS+=(--dry-run) +fi + +# Write a temporary .npmrc for authentication +NPMRC_BAK="" +UI_NPMRC="${REPO_ROOT}/ui/.npmrc" +if [[ -f "${UI_NPMRC}" ]]; then + NPMRC_BAK="$(cat "${UI_NPMRC}")" +fi + +# Append auth token for npmjs.org +if ! grep -q "registry.npmjs.org/:_authToken" "${UI_NPMRC}" 2>/dev/null; then + echo "//registry.npmjs.org/:_authToken=\${NPM_PUBLISH_TOKEN}" >> "${UI_NPMRC}" +fi + +cleanup_npmrc() { + if [[ -n "${NPMRC_BAK}" ]]; then + echo "${NPMRC_BAK}" > "${UI_NPMRC}" + else + # Remove the line we added + sed -i.bak '/registry.npmjs.org\/:_authToken/d' "${UI_NPMRC}" && rm -f "${UI_NPMRC}.bak" + fi +} +trap cleanup_npmrc EXIT + +# Publish order matters: dependencies first +echo "==> Publishing @aaif/goose-acp" +(cd "${REPO_ROOT}/ui" && pnpm publish "${PUBLISH_ARGS[@]}" acp) + +echo "==> Publishing native binary packages" +for plat in darwin-arm64 darwin-x64 linux-arm64 linux-x64; do + pkg="goose-binary/goose-binary-${plat}" + echo " Publishing @aaif/goose-binary-${plat}" + (cd "${REPO_ROOT}/ui" && pnpm publish "${PUBLISH_ARGS[@]}" "${pkg}") +done + +echo "==> Publishing @aaif/goose" +(cd "${REPO_ROOT}/ui" && pnpm publish "${PUBLISH_ARGS[@]}" text) + +echo "" +if [[ "${DRY_RUN}" == "true" ]]; then + echo "✅ Dry run complete. Pass --real to publish for real." +else + echo "✅ All packages published to ${REGISTRY}" +fi diff --git a/ui/text/package.json b/ui/text/package.json index 703e3e56..43136865 100644 --- a/ui/text/package.json +++ b/ui/text/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose", - "version": "0.1.2", + "version": "0.2.0", "description": "Goose - an open-source AI agent", "license": "Apache-2.0", "repository": {