From a3b43db6b1fd4b74a8c94c2f33ae7bb9b1473f95 Mon Sep 17 00:00:00 2001 From: Mackenzie Zastrow Date: Tue, 16 Jun 2026 10:21:59 -0400 Subject: [PATCH] feat: migrate to napi-rs v3 and update release workflow - Upgrade napi/napi-derive to v3, CLI to @napi-rs/cli@^3.7.2 - Derive version from git tag via .github/actions/stamp-version - Switch npm publish to OIDC trusted publishing (no NPM_TOKEN) - Split npm publish into per-platform matrix (independently retriable) - Add pre-publish safety gates (node-pack, python-inspect) - Upload artifacts for local testing before publish - Fix aarch64-linux cross-compile for both Node and Python - Bump CI action versions (checkout v6, setup-python v6, setup-node v6) --- .github/actions/stamp-version/action.yml | 62 ++++ .github/workflows/ci.yml | 14 +- .github/workflows/release.yml | 357 +++++++++++++++++++---- .gitignore | 5 + Cargo.lock | 105 +++++-- Cargo.toml | 11 +- package.json | 10 +- pyproject.toml | 4 +- 8 files changed, 469 insertions(+), 99 deletions(-) create mode 100644 .github/actions/stamp-version/action.yml diff --git a/.github/actions/stamp-version/action.yml b/.github/actions/stamp-version/action.yml new file mode 100644 index 0000000..22eda61 --- /dev/null +++ b/.github/actions/stamp-version/action.yml @@ -0,0 +1,62 @@ +name: Stamp version +description: >- + Write the release version (derived from the git tag) into the package + manifests. The git tag is the single source of truth for the version; the + committed manifests carry a 0.0.0 placeholder and never hold a real version. + This action stamps the tag's version into Cargo.toml, pyproject.toml, and + package.json right after checkout, before the build tools (maturin / napi) + read them. Run it in every job that builds, packs, or publishes. + +# ── Why stamp at all (vs. committing the version) ─────────────────────────── +# Committing a concrete version in three manifests means three things to bump +# in lockstep on every release, and they silently drift. Deriving from the tag +# keeps one source of truth and makes "cut a vX.Y.Z tag" the entire release +# action. (See the `version` job in release.yml, which parses + validates the +# tag and passes it here.) +# +# ── Why a tiny perl edit and not a dedicated tool ─────────────────────────── +# - Cargo has no built-in "version from git tag". The official option is +# `cargo set-version` from the `cargo-edit` crate, but that means a +# `cargo install cargo-edit` step (network + ~30-60s) in every build job just +# to rewrite one line. The crate is not published to crates.io (we release to +# PyPI + npm only), so Cargo.toml's version exists solely to feed maturin's +# wheel metadata — not worth an extra toolchain dependency. +# - The perl one-liner rewrites ONLY the first top-level `version = "..."` line +# (the [package] / [project] version). Dependency and path-dep versions +# (e.g. napi = { version = "3" }, the strands-shell-macros path dep) appear +# later and are left untouched. perl -i ships on both Linux and macOS GitHub +# runners, so it works across the whole build matrix. +# - package.json uses npm's own `npm version` command (the official mechanism, +# matching the strands-agents golden path), not a regex. + +inputs: + version: + description: The version to stamp (no leading "v"), e.g. 0.2.0 + required: true + +runs: + using: composite + steps: + - name: Stamp version into manifests + shell: bash + env: + V: ${{ inputs.version }} + run: | + set -euo pipefail + # Rewrite the first `version = "..."` in each TOML (the package version). + perl -i -pe 'if (!$done && /^version\s*=\s*"/) { s/"[^"]*"/"$ENV{V}"/; $done=1 }' Cargo.toml + perl -i -pe 'if (!$done && /^version\s*=\s*"/) { s/"[^"]*"/"$ENV{V}"/; $done=1 }' pyproject.toml + echo "Stamped version $V:" + echo " Cargo.toml=$(grep -m1 '^version' Cargo.toml)" + echo " pyproject.toml=$(grep -m1 '^version' pyproject.toml)" + # package.json via npm's official version command. This is only needed by + # the Node jobs; the Python build jobs (python-wheels/sdist) set up Python + # but not Node — and python-wheels runs in a manylinux container with no + # npm — so guard on npm being available and skip otherwise. + if command -v npm >/dev/null 2>&1; then + # --allow-same-version so re-runs against an already-stamped tree pass. + npm version "$V" --no-git-tag-version --allow-same-version >/dev/null + echo " package.json=$(node -p "require('./package.json').version")" + else + echo " package.json=skipped (npm not available — Python-only job)" + fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e00722..6fc4f64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -47,7 +47,7 @@ jobs: python: ["3.10", "3.11", "3.12", "3.13", "3.14"] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -58,7 +58,7 @@ jobs: key: py-${{ matrix.os }}-${{ matrix.python }} - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python }} @@ -78,7 +78,7 @@ jobs: name: Security audit runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -99,7 +99,7 @@ jobs: continue-on-error: true - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: "20" @@ -119,7 +119,7 @@ jobs: node: ["20", "22", "24"] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -130,7 +130,7 @@ jobs: key: node-${{ matrix.os }}-${{ matrix.node }} - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ matrix.node }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 87ce620..3b18191 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,46 +15,36 @@ permissions: id-token: write # PyPI Trusted Publishing jobs: - # ── Guard: tag must match the version declared in package manifests ────── - verify-version: - name: Verify tag matches manifest versions + # ── Derive the release version from the tag (single source of truth) ───── + # Manifests carry a 0.0.0 placeholder; the tag is authoritative. This job + # parses + validates the version and exposes it; build/pack/publish jobs then + # stamp it into the manifests via .github/actions/stamp-version. + version: + name: Derive version from tag runs-on: ubuntu-latest + outputs: + version: ${{ steps.derive.outputs.version }} steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.ref || github.ref }} - - - name: Compare tag against Cargo.toml, pyproject.toml, package.json + - name: Derive and validate version + id: derive run: | set -euo pipefail ref="${{ inputs.ref || github.ref_name }}" # Strip refs/tags/ prefix if present, then leading "v". tag="${ref#refs/tags/}" tag="${tag#v}" - - cargo_v=$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/') - pyproject_v=$(grep -m1 '^version' pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/') - package_v=$(node -p "require('./package.json').version") - - echo "tag=$tag" - echo "Cargo.toml=$cargo_v" - echo "pyproject.toml=$pyproject_v" - echo "package.json=$package_v" - - fail=0 - for v in "$cargo_v" "$pyproject_v" "$package_v"; do - if [ "$v" != "$tag" ]; then - fail=1 - fi - done - if [ "$fail" -ne 0 ]; then - echo "::error::Tag $tag does not match all manifest versions. Bump versions and re-tag." + # Accept semver core plus optional pre-release/build (e.g. 0.2.0, + # 1.2.3-rc.1). Reject anything that isn't a release version. + if [[ ! "$tag" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then + echo "::error::Tag '$ref' does not yield a valid version (got '$tag')." exit 1 fi + echo "version=$tag" >> "$GITHUB_OUTPUT" + echo "Release version: $tag" # ── Build native wheels for PyPI (one job per target) ──────────────────── python-wheels: - needs: verify-version + needs: version name: Python wheel (${{ matrix.target }}) strategy: fail-fast: false @@ -72,70 +62,159 @@ jobs: manylinux: "2_28" runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ inputs.ref || github.ref }} - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.12" + - name: Stamp version + uses: ./.github/actions/stamp-version + with: + version: ${{ needs.version.outputs.version }} + - name: Build wheel uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} - args: --release --out dist --strip + # --find-interpreter is required for cross-compiled targets: inside the + # manylinux cross container maturin can't auto-detect an interpreter for + # the non-host arch, so it must enumerate the ones the image ships. + args: --release --out dist --strip --find-interpreter manylinux: ${{ matrix.manylinux || 'auto' }} sccache: "true" - name: Upload wheel artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: python-wheel-${{ matrix.target }} path: dist/*.whl python-sdist: - needs: verify-version + needs: version name: Python sdist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ inputs.ref || github.ref }} - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.12" + - name: Stamp version + uses: ./.github/actions/stamp-version + with: + version: ${{ needs.version.outputs.version }} + - name: Build sdist uses: PyO3/maturin-action@v1 with: command: sdist args: --out dist - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: python-sdist path: dist/*.tar.gz + # ── Inspection gate: assemble every dist and verify before publishing ──── + python-inspect: + name: Inspect Python dists + needs: [python-wheels, python-sdist] + runs-on: ubuntu-latest + steps: + # Only the Python dists — without the pattern, merge-multiple would also + # pull the node-addon-*.node artifacts and break `twine check dist/*`. + - uses: actions/download-artifact@v8 + with: + path: dist + pattern: python-* + merge-multiple: true + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: List contents of every wheel and sdist + run: | + set -euo pipefail + for f in dist/*.whl; do + echo "::group::$f"; python -m zipfile -l "$f"; echo "::endgroup::" + done + for f in dist/*.tar.gz; do + echo "::group::$f"; tar tzf "$f"; echo "::endgroup::" + done + + - name: Verify the dist set covers all platform targets + # Maturin's --find-interpreter builds a wheel per discovered Python + # version, so the exact count varies as runners update. Instead of an + # exact count, verify that every matrix target produced at least one + # wheel and that the sdist exists. + run: | + set -euo pipefail + echo "Dists:"; ls -1 dist/ + missing=() + for target in aarch64-apple-darwin x86_64-apple-darwin x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do + # Maturin encodes the target in the wheel filename's platform tag: + # aarch64-apple-darwin → macosx_*_arm64 + # x86_64-apple-darwin → macosx_*_x86_64 + # x86_64-unknown-linux-gnu → manylinux_*_x86_64 + # aarch64-unknown-linux-gnu → manylinux_*_aarch64 + case "$target" in + aarch64-apple-darwin) pattern="macosx_*_arm64" ;; + x86_64-apple-darwin) pattern="macosx_*_x86_64" ;; + x86_64-unknown-linux-gnu) pattern="manylinux_*_x86_64" ;; + aarch64-unknown-linux-gnu) pattern="manylinux_*_aarch64" ;; + esac + if ! ls dist/*${pattern}*.whl >/dev/null 2>&1; then + missing+=("$target") + fi + done + if [ ! -f dist/*.tar.gz ]; then + missing+=("sdist") + fi + if [ ${#missing[@]} -gt 0 ]; then + echo "::error::Missing dists for: ${missing[*]}" + exit 1 + fi + echo "All platform targets covered + sdist present." + + - name: twine check (metadata + README rendering) + run: | + pip install twine + twine check dist/* + + - name: Upload combined dists for manual inspection + uses: actions/upload-artifact@v7 + with: + name: pypi-dist-bundle + path: dist/* + python-publish: name: Publish to PyPI - needs: [python-wheels, python-sdist] + needs: python-inspect runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/strands-shell + permissions: + id-token: write steps: - - uses: actions/download-artifact@v4 + # Publish exactly the bundle that python-inspect verified. + - uses: actions/download-artifact@v8 with: + name: pypi-dist-bundle path: dist - merge-multiple: true - uses: pypa/gh-action-pypi-publish@release/v1 # ── Build native node addons for npm (one job per target) ──────────────── node-builds: - needs: verify-version + needs: version name: Node addon (${{ matrix.target }}) strategy: fail-fast: false @@ -151,11 +230,11 @@ jobs: target: aarch64-unknown-linux-gnu runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ inputs.ref || github.ref }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: "20" @@ -163,6 +242,11 @@ jobs: with: targets: ${{ matrix.target }} + - name: Stamp version + uses: ./.github/actions/stamp-version + with: + version: ${{ needs.version.outputs.version }} + - name: Install cross-compile deps (linux aarch64) if: matrix.target == 'aarch64-unknown-linux-gnu' run: | @@ -175,45 +259,200 @@ jobs: - name: napi build # --js/--dts must match package.json's build script: write the generated # loader to native.* and leave the hand-authored index.* wrapper intact. - run: npx napi build --platform --release --features node --cargo-flags=--lib --js native.js --dts native.d.ts --target ${{ matrix.target }} + # When cross-compiling to aarch64-linux, Cargo must link with the + # aarch64 cross GCC; otherwise it falls back to the host x86-64 `cc` and + # rust-lld rejects the object files as "incompatible with elf64-x86-64". + # The var is harmless on native targets (empty -> ignored). + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && 'aarch64-linux-gnu-gcc' || '' }} + # napi-rs v3: cargo flags go after `--` (v2's --cargo-flags was removed). + # `--lib` keeps cargo from also building the [[bin]] targets (one needs + # the wasm feature) when only the cdylib addon is wanted. + run: npx napi build --platform --release --features node --js native.js --dts native.d.ts --target ${{ matrix.target }} -- --lib - name: Upload native artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: node-addon-${{ matrix.target }} - path: "*.node" + # Ship the generated loader (native.js/.d.ts) alongside the binary. + # These are gitignored build outputs, so the pack/publish jobs (which + # don't run napi build) have no other way to get them — the main + # package's index.js does `require('./native.js')`. The loader is + # platform-independent and identical across targets, so downloading + # with merge-multiple just overwrites it with the same content. + path: | + *.node + native.js + native.d.ts - node-publish: - name: Publish to npm - needs: node-builds + # ── Pack all 5 npm packages (fan-in from the per-platform builds) ───────── + # Assembles the exact publishable set — the main @strands-agents/shell package + # plus the 4 per-platform packages — using the napi-rs v3 release flow, then + # packs each to a .tgz and uploads them. Download the `npm-packages` artifact + # to install/test the real tarballs locally before any publish happens. + node-pack: + name: Pack npm packages + needs: [version, node-builds] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ inputs.ref || github.ref }} + persist-credentials: false - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: "20" - registry-url: "https://registry.npmjs.org" + + - name: Stamp version + uses: ./.github/actions/stamp-version + with: + version: ${{ needs.version.outputs.version }} - name: npm install run: npm install - name: Download all native artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: artifacts pattern: node-addon-* merge-multiple: true - - name: Move artifacts into place - run: mv artifacts/*.node ./ + - name: Restore generated loader into the main package + # native.js/.d.ts are gitignored build outputs uploaded by node-builds; + # the main package's index.js requires('./native.js'), so put them at the + # repo root before packing. (This job doesn't run napi build.) + run: cp artifacts/native.js artifacts/native.d.ts ./ - - name: napi prepublish - run: npx napi prepublish -t npm --skip-gh-release + - name: Assemble npm packages (napi-rs v3, no publish) + # The same napi flow node-publish uses, minus the registry push: + # - create-npm-dirs scaffolds npm// with os/cpu/libc package.json + # - artifacts copies each *.node from ./artifacts into its npm// + # (--output-dir is the INPUT location of the .node files; --npm-dir the OUTPUT) + # - pre-publish --skip-optional-publish wires the main package's + # optionalDependencies to the platform packages WITHOUT publishing, + # so the packed set matches exactly what node-publish would ship. + run: | + npx napi create-npm-dirs + npx napi artifacts --output-dir ./artifacts --npm-dir ./npm + npx napi pre-publish -t npm --no-gh-release --skip-optional-publish - - name: npm publish - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Pack all packages + run: | + set -euo pipefail + mkdir -p dist-npm + npm pack --pack-destination dist-npm + for d in npm/*/; do (cd "$d" && npm pack --pack-destination "$GITHUB_WORKSPACE/dist-npm"); done + + - name: Verify the packaged set matches the publish matrix + # Guard against drift: the publish stage uses a static matrix of the 4 + # platform packages (+ the main package = 5 total). If package.json's + # napi.targets gains/loses a target, the packed count changes here and + # this fails the run BEFORE anything is published — prompting whoever + # changed the targets to update node-publish-platform's matrix to match. + run: | + set -euo pipefail + expected=5 + actual=$(ls dist-npm/*.tgz | wc -l | tr -d ' ') + echo "Packed tarballs ($actual):"; ls -1 dist-npm/*.tgz + if [ "$actual" -ne "$expected" ]; then + echo "::error::Expected $expected npm packages (1 main + 4 platform) but packed $actual. If napi.targets changed, update node-publish-platform's matrix to match." + exit 1 + fi + + - name: List every tarball's contents + run: | + set -euo pipefail + for tgz in dist-npm/*.tgz; do + echo "::group::$tgz"; tar tzf "$tgz"; echo "::endgroup::" + done + + - name: Upload all npm packages for manual inspection + uses: actions/upload-artifact@v7 + with: + name: npm-packages + path: dist-npm/*.tgz + + # ── Publish the 4 per-platform packages (one independently retriable job each) + # Each leg publishes exactly one tarball that node-inspect packed + uploaded, + # so a flaky single platform can be re-run on its own via "Re-run failed jobs" + # without touching the others. The matrix is static — its entries must match + # package.json's napi.targets; node-inspect's count check fails the run before + # this stage if they drift. fail-fast: false so one platform failing doesn't + # cancel the rest. + node-publish-platform: + name: Publish ${{ matrix.pkg }} + needs: [version, node-pack] + runs-on: ubuntu-latest + # OIDC trusted publishing (no NPM_TOKEN) — matches the strands-agents golden + # path (see harness-sdk). Requires id-token: write and a registered trusted + # publisher on npmjs.com for every package published. + environment: + name: npm + permissions: + id-token: write + contents: read + strategy: + fail-fast: false + matrix: + pkg: + - strands-agents-shell-darwin-x64 + - strands-agents-shell-darwin-arm64 + - strands-agents-shell-linux-x64-gnu + - strands-agents-shell-linux-arm64-gnu + env: + V: ${{ needs.version.outputs.version }} + steps: + # No checkout/build — publish the exact tarball node-inspect uploaded. + - uses: actions/setup-node@v6 + with: + node-version: "20" + registry-url: "https://registry.npmjs.org" + + - name: Update npm to latest + # Trusted publishing needs a recent npm (>= 11.5.1). + run: npm install -g npm@latest + + - name: Download the inspected npm packages + uses: actions/download-artifact@v8 + with: + name: npm-packages + path: dist-npm + + - name: Publish ${{ matrix.pkg }} + # ./ prefix: npm treats a bare "dir/file.tgz" as a git spec. + run: npm publish "./dist-npm/${{ matrix.pkg }}-${V}.tgz" --access public + + # ── Publish the main package last ──────────────────────────────────────── + # Gated on all platform packages succeeding, since the main package's + # optionalDependencies reference them. + node-publish-main: + name: Publish @strands-agents/shell + needs: [version, node-publish-platform] + runs-on: ubuntu-latest + environment: + name: npm + url: https://www.npmjs.com/package/@strands-agents/shell + permissions: + id-token: write + contents: read + env: + V: ${{ needs.version.outputs.version }} + steps: + - uses: actions/setup-node@v6 + with: + node-version: "20" + registry-url: "https://registry.npmjs.org" + + - name: Update npm to latest + run: npm install -g npm@latest + + - name: Download the inspected npm packages + uses: actions/download-artifact@v8 + with: + name: npm-packages + path: dist-npm + + - name: Publish @strands-agents/shell + run: npm publish "./dist-npm/strands-agents-shell-${V}.tgz" --access public diff --git a/.gitignore b/.gitignore index f796214..de8086c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ __pycache__/ *.egg-info/ .pytest_cache/ +uv.lock node_modules/ package-lock.json # Editor / AI-agent local config @@ -17,3 +18,7 @@ CLAUDE.md # The napi-generated loader is emitted to native.js / native.d.ts (artifacts). /native.js /native.d.ts +# napi-generated per-platform package dirs and locally packed tarballs. +/npm/ +/dist-npm/ +/artifacts/ diff --git a/Cargo.lock b/Cargo.lock index bce8e4d..b11def0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,9 +269,9 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "convert_case" -version = "0.6.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" dependencies = [ "unicode-segmentation", ] @@ -284,13 +284,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "ctor" -version = "0.2.9" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" -dependencies = [ - "quote", - "syn", -] +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" [[package]] name = "displaydoc" @@ -381,6 +377,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.32" @@ -388,6 +399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -396,6 +408,40 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + [[package]] name = "futures-task" version = "0.3.32" @@ -408,8 +454,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -814,9 +865,9 @@ checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libloading" -version = "0.8.9" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" dependencies = [ "cfg-if", "windows-link", @@ -942,15 +993,17 @@ dependencies = [ [[package]] name = "napi" -version = "2.16.17" +version = "3.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3" +checksum = "26d3c7dd60231116a47854321c9ac8df6f13435d11aa3a59d8533a76e07a3730" dependencies = [ "bitflags", "ctor", - "napi-derive", + "futures", + "napi-build", "napi-sys", - "once_cell", + "nohash-hasher", + "rustc-hash", "tokio", ] @@ -962,12 +1015,12 @@ checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" [[package]] name = "napi-derive" -version = "2.16.13" +version = "3.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" dependencies = [ - "cfg-if", "convert_case", + "ctor", "napi-derive-backend", "proc-macro2", "quote", @@ -976,24 +1029,22 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.75" +version = "5.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" dependencies = [ "convert_case", - "once_cell", "proc-macro2", "quote", - "regex", "semver", "syn", ] [[package]] name = "napi-sys" -version = "2.4.0" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" dependencies = [ "libloading", ] @@ -1019,6 +1070,12 @@ dependencies = [ "libc", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "num-traits" version = "0.2.19" @@ -1603,7 +1660,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "strands-shell" -version = "0.1.0" +version = "0.0.0" dependencies = [ "async-trait", "axum", diff --git a/Cargo.toml b/Cargo.toml index adcc0f2..c6bb0a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,8 @@ [package] name = "strands-shell" -version = "0.1.0" +# Placeholder — the real version comes from the release git tag, stamped at +# build time by .github/actions/stamp-version. Do not bump this by hand. +version = "0.0.0" edition = "2024" description = "A virtual shell sandbox for AI agents" license = "Apache-2.0" @@ -44,8 +46,8 @@ jaq-json = { version = "1", features = ["serde_json"] } # ----- Native-only dependencies ----- [target.'cfg(not(target_arch = "wasm32"))'.dependencies] inventory = "0.3" -napi = { version = "2", features = ["napi9", "tokio_rt"], optional = true } -napi-derive = { version = "2", optional = true } +napi = { version = "3", features = ["napi9", "tokio_rt"], optional = true } +napi-derive = { version = "3", optional = true } pyo3 = { version = "0.29", features = ["extension-module"], optional = true } reqwest = { version = "0.12", features = ["rustls-tls"], default-features = false } rustyline = "17.0" @@ -67,5 +69,8 @@ members = [".", "strands-shell-macros", "xtask"] [dev-dependencies] axum = "0.8" +# napi-build stays on 2.x — the build-script helper is versioned +# independently of the napi/napi-derive runtime crates and has no 3.x release +# (latest is 2.3.2, which is what pairs with napi 3.x). [build-dependencies] napi-build = { version = "2", optional = true } diff --git a/package.json b/package.json index a4ffcaf..470de1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@strands-agents/shell", - "version": "0.1.0", + "version": "0.0.0", "description": "Strands Shell — a virtual shell sandbox for AI agents (Node.js bindings)", "license": "Apache-2.0", "repository": { @@ -16,7 +16,7 @@ "index.d.ts", "native.js", "native.d.ts", - "*.node" + "NOTICE" ], "napi": { "binaryName": "strands-shell", @@ -32,13 +32,13 @@ }, "scripts": { "host-triple": "node -p \"require('child_process').execSync('rustc -vV').toString().match(/host: (.+)/)[1]\"", - "build": "napi build --platform --release --features node --cargo-flags=--lib --js native.js --dts native.d.ts --target $(npm run --silent host-triple)", - "build:debug": "napi build --platform --features node --cargo-flags=--lib --js native.js --dts native.d.ts --target $(npm run --silent host-triple)", + "build": "napi build --platform --release --features node --js native.js --dts native.d.ts --target $(npm run --silent host-triple) -- --lib", + "build:debug": "napi build --platform --features node --js native.js --dts native.d.ts --target $(npm run --silent host-triple) -- --lib", "test": "node --test tests/js/*.mjs", "typecheck": "tsc --noEmit" }, "devDependencies": { - "@napi-rs/cli": "^2.18.4", + "@napi-rs/cli": "^3.7.2", "typescript": "^5.6.0" } } diff --git a/pyproject.toml b/pyproject.toml index b65896b..150b508 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,9 @@ build-backend = "maturin" [project] name = "strands-shell" -version = "0.1.0" +# Placeholder — the real version comes from the release git tag, stamped at +# build time by .github/actions/stamp-version. Do not bump this by hand. +version = "0.0.0" description = "A virtual shell for AI agents" readme = "README.md" requires-python = ">=3.10"