diff --git a/.agents/skills/maintain/SKILL.md b/.agents/skills/maintain/SKILL.md new file mode 100644 index 000000000..cd771c710 --- /dev/null +++ b/.agents/skills/maintain/SKILL.md @@ -0,0 +1,35 @@ +--- +name: maintain +description: Analyze, fix, validate, and ship Bashkit maintenance through green CI and merge. Trigger on run maintenance, maintain, maintainace, or maintaiance; use analysis-only mode only when explicitly requested. +metadata: + internal: true +--- + +# Bashkit maintenance + +A request to run maintenance authorizes the full outcome: analyze the repository, +fix findings, validate, push, open a PR, resolve review/CI failures, and squash-merge +when every required check is green. A local commit or a list of follow-up issues +is not completion. Explicit analysis-only requests omit edits and shipping. + +Read `knowledge/operations/maintenance.md` in the Bashkit checkout and execute its +checklist, then use the repository's `ship` skill. Sync from latest origin/main +before editing. Use parallel agents for independent reviews and fixes when useful. + +Keep findings in the active pass and fix root causes. A large diff, missing audit, +failed local check, or long build is work to complete, not a reason to defer. +Use a suitable environment for platform-specific checks. Never weaken tests, +security limits, or supply-chain criteria to ship an upgrade. If the newest +upstream version cannot preserve a required security contract, establish the +incompatibility, retain the newest safe version with a tested/documented pin, +and complete the rest of the pass; do not replace security checks with weaker ones. + +Do not create deferral issues instead of completing maintenance unless the user +explicitly requests a deferred scope or a genuine external blocker requires it. +Only stop for an actual missing permission, unavailable dependency/service, or +user decision that cannot be resolved within the authorized task. Report concrete +evidence and continue independent work. CI failures require diagnosis and fixes; +never merge red CI or claim success while a required check is unfinished. + +The final response identifies the merged PR, key fixes, verification, and any +explicitly accepted upstream constraints. Keep the spec's pass record factual. diff --git a/.agents/skills/ship/SKILL.md b/.agents/skills/ship/SKILL.md index e28613a91..0e9cbbbb6 100644 --- a/.agents/skills/ship/SKILL.md +++ b/.agents/skills/ship/SKILL.md @@ -1,6 +1,6 @@ --- name: ship -description: Run the full ship flow — verify quality, ensure test coverage, update artifacts, smoke test, push, create PR, and merge when CI is green. Trigger when user says "ship", "ship it", "fix and ship", or asks to push and merge a branch. +description: Run the full ship flow — verify quality, ensure test coverage, update artifacts, smoke test, push, create PR, and merge when CI is green. Trigger when user says "ship", "ship it", "fix and ship", asks to push and merge a branch, or runs Bashkit maintenance. user_invocable: true metadata: internal: true @@ -106,14 +106,13 @@ If smoke testing reveals issues, fix them and loop back to Phase 2 (tests must s git fetch origin main && git rebase origin/main ``` -- If rebase fails with conflicts, abort and tell the user to resolve manually +- Resolve rebase conflicts and rerun affected checks. Ask only when an unresolved conflict requires a user decision. ```bash just pre-pr ``` -- If it fails, run `just fmt` to auto-fix, then retry once -- If still failing, stop and report +- Diagnose failing checks, fix their root causes, and rerun. Do not bypass hooks or weaken tests/audit policy. ### Phase 6: Push and PR @@ -138,9 +137,9 @@ If a PR already exists, update it if needed and report its URL. ### Phase 7: Wait for CI and Merge -- Check CI status with `gh pr checks` (poll every 30s, up to 15 minutes) -- If CI is green, merge with `gh pr merge --squash --auto` -- If CI fails, report the failing checks and stop +- Check CI status with bounded waits. Continue until the checks finish; elapsed time alone is not a blocker. +- If all required CI is green and review comments are resolved, squash-merge and verify the PR actually merged. Enabling auto-merge alone is not completion. +- If CI fails, inspect logs, fix the root cause, push the fix, and wait for the new checks. - **NEVER** merge when CI is red ### Phase 8: Post-merge diff --git a/.claude/commands/maintain.md b/.claude/commands/maintain.md index 3f7143153..8e5a94ad1 100644 --- a/.claude/commands/maintain.md +++ b/.claude/commands/maintain.md @@ -1,113 +1,12 @@ -Run the pre-release maintenance checklist from `knowledge/operations/maintenance.md`. Find and fix all issues before reporting. - -## Arguments - -- `$ARGUMENTS` - Optional: scope to a specific section (e.g. "dependencies", "docs", "simplification"). If omitted, run all sections. - -## Goals - -Each section below is an outcome to achieve, not a script to follow. Use whatever tools and approaches make sense to verify and fix. - -### 1. Dependencies are current and clean - -Ensure all direct dependencies are at their latest versions — including major/breaking upgrades. Don't just run `cargo update`; also check `cargo outdated` for major version bumps and update the version constraints in `Cargo.toml` accordingly. - -For each outdated dependency (including major bumps): -1. Bump the version constraint in workspace `Cargo.toml` (or crate-level if not in workspace) -2. Run `cargo build` — fix compilation errors from API changes -3. Run `cargo test` — fix test failures -4. If an upgrade requires non-trivial refactoring (>50 lines changed), defer it to a tracked GitHub issue - -After all bumps, run `cargo update` to lock latest patch versions. - -Ensure no CVEs exist and license/advisory/supply-chain checks pass. - -Key tools: `cargo update`, `cargo outdated`, `cargo audit`, `cargo deny check`, `just vet` - -### 2. Security posture is solid - -Ensure the threat model covers all features, security tests exist for all mitigated threats, and no OWASP-style issues exist in the codebase. - -Key references: `knowledge/security/threat-model.md`, `knowledge/security/security-testing.md`, `crates/bashkit/docs/threat-model.md` - -### 3. Tests are comprehensive and green - -All tests pass, no gaps for recent features, bash compatibility holds, coverage has no major holes. - -Key tools: `just test`, `just check-bash-compat` - -### 4. Documentation matches reality - -All docs (rustdoc, guides, public docs in `docs/`, Python, README, CONTRIBUTING, CHANGELOG) accurately reflect current code. Command counts, feature lists, CLI flags, security boundaries, API signatures, and examples are correct. - -Fix any drift — update the docs, not the code (unless the code is wrong). - -### 5. Examples work end-to-end - -All Rust examples compile and run. Feature-gated examples (python, git) work. Python agent examples run successfully. - -Key tools: `cargo run --example `, feature-gated variants - -### 6. Specs reflect reality - -Every spec status is accurate. Implementation status tables match code. No orphaned TODOs. New features have spec entries. - -### 7. Code is clean - -Formatted, no clippy warnings, no stale TODOs, no dead code or unused deps. - -Key tools: `cargo fmt --check`, `cargo clippy --all-targets --all-features -- -D warnings` - -### 8. Code is as simple as possible - -Review the codebase (focus on recently changed areas) for simplification opportunities: - -- **Duplication** — repeated patterns that should share a helper -- **Over-engineering** — abstractions, indirection, or configurability that doesn't serve current needs -- **Complexity** — deeply nested logic, long match arms, convoluted control flow -- **Dead code** — unused functions, unreachable branches, commented-out code -- **Naming** — unclear or misleading names - -Make the simplifications. Run tests after each change. The goal is less code that does the same thing. - -### 9. Agent configuration is accurate - -`AGENTS.md` and `CLAUDE.md` reflect current specs, commands, tooling, and workflows. - -### 10. All CI is healthy (HARD GATE) - -**This section is a blocker.** The maintenance pass MUST NOT be marked complete -while any of these checks are red. - -1. **CI on main is green** — check the latest CI run on the `main` branch. If - any job (Audit, Test, Lint, Examples, Fuzz Compile Check) fails, fix it - before proceeding. Common failures: `cargo vet` missing certifications, - dependency audit advisories, clippy warnings. -2. **Nightly workflow green** for past 7 days. -3. **Fuzz workflow green** for past 7 days. If a fuzz target crashes, open a - GitHub issue with the crash artifact, reproduction command, and base64 input. -4. Fuzz targets compile. Git-sourced deps resolve. - -Key tools: -- `gh run list --workflow=ci.yml --branch=main --limit 5` (CI on main) -- `gh run list --workflow=nightly.yml --limit 7` (nightly) -- `gh run list --workflow=fuzz.yml --limit 7` (fuzz) -- `gh api repos/OWNER/REPO/actions/runs/RUN_ID/jobs` (inspect failed jobs) - -If failures persist >2 days, escalate per `knowledge/operations/maintenance.md`. -If the agent cannot fix a failure, it MUST open a GitHub issue and report the -pass as blocked — never silently skip. - -## Execution - -- Run all sections (or scoped subset from `$ARGUMENTS`) -- Fix issues as you find them — don't just report -- Commit fixes incrementally with conventional commit messages -- After all sections complete, report a summary of findings and fixes -- If any section has unfixable issues, report them clearly with recommended next steps - -## Notes - -- This is a goal-based checklist. The spec (`knowledge/operations/maintenance.md`) defines *what* must be true. This skill defines *how* to verify and fix. -- Use parallel agents for independent sections when possible. -- For scoped runs, still verify that fixes don't break other areas (`just test` at minimum). +Analyze, fix, and ship Bashkit maintenance. "Run maintenance" (including +"maintainace" and "maintaiance") requests the complete shipped outcome. + +Read `.agents/skills/maintain/SKILL.md`, execute the checklist in +`knowledge/operations/maintenance.md`, then run `.agents/skills/ship/SKILL.md` +through green CI and squash-merge. `$ARGUMENTS` may scope the maintenance area; +an explicit analysis-only request is the only default exception to fixing/shipping. + +Use parallel agents for independent sections when useful. Do not replace fixes, +audits, or validation with deferral issues or stop at a local commit. Preserve +security contracts, use an appropriate validation environment, fix CI failures, +and never merge with red checks. diff --git a/.deepsec/package.json b/.deepsec/package.json index 69738a686..75ca35d9d 100644 --- a/.deepsec/package.json +++ b/.deepsec/package.json @@ -7,6 +7,6 @@ "workspaces": [], "packageManager": "pnpm@9.15.4", "dependencies": { - "deepsec": "2.1.2" + "deepsec": "2.3.9" } } diff --git a/.github/workflows/c-api-binaries.yml b/.github/workflows/c-api-binaries.yml index e9468fa08..7d0cebfaf 100644 --- a/.github/workflows/c-api-binaries.yml +++ b/.github/workflows/c-api-binaries.yml @@ -1,3 +1,4 @@ +# THREAT[TM-INF-027]: build only the immutable SHA exported by tag validation. # Native C ABI archives, dispatched from the verified release workflow. # Windows must initialize the Visual Studio environment before using lib.exe/cl.exe. # Existing tags may be rebuilt only with the tagged or current main workflow definition. @@ -20,6 +21,7 @@ jobs: runs-on: ubuntu-latest outputs: tag: ${{ steps.validate.outputs.tag }} + sha: ${{ steps.source.outputs.sha }} steps: - name: Validate workflow input id: validate @@ -38,18 +40,21 @@ jobs: fetch-depth: 0 - name: Verify tag source + id: source env: RELEASE_TAG: ${{ steps.validate.outputs.tag }} run: | + git fetch --force origin main:refs/remotes/origin/main \ + "refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG" + TAG_SHA=$(git rev-list -n 1 "$RELEASE_TAG") + # Validate and build this exact commit even if the tag later moves. + git checkout --detach "$TAG_SHA" VERSION="${RELEASE_TAG#v}" CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') if [ "$VERSION" != "$CARGO_VERSION" ]; then echo "Error: tag $RELEASE_TAG does not match Cargo.toml $CARGO_VERSION" >&2 exit 1 fi - git fetch --force origin main:refs/remotes/origin/main \ - "refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG" - TAG_SHA=$(git rev-list -n 1 "$RELEASE_TAG") MAIN_SHA=$(git rev-parse origin/main) if [ "$GITHUB_SHA" != "$TAG_SHA" ] && [ "$GITHUB_SHA" != "$MAIN_SHA" ]; then echo "Error: workflow ref must be $RELEASE_TAG or current main" >&2 @@ -59,6 +64,7 @@ jobs: echo "Error: $RELEASE_TAG is not reachable from origin/main" >&2 exit 1 fi + echo "sha=$TAG_SHA" >> "$GITHUB_OUTPUT" build: name: Build C API (${{ matrix.target }}) @@ -94,7 +100,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - ref: ${{ needs.validate-tag.outputs.tag }} + ref: ${{ needs.validate-tag.outputs.sha }} persist-credentials: false - name: Install Rust toolchain diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1ff1832a..adbfdb6c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,9 +7,10 @@ on: branches: [main] workflow_call: +# CI executes repository code and has no consumer of checks-write permission. +# Checkout credentials must not remain available to build scripts or tests. permissions: contents: read - checks: write env: CARGO_TERM_COLOR: always @@ -22,6 +23,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -76,6 +79,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -109,6 +114,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -138,6 +145,8 @@ jobs: timeout-minutes: 30 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -147,7 +156,7 @@ jobs: - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 - name: Install wasm-bindgen-cli - run: cargo install wasm-bindgen-cli --version 0.2.126 --locked + run: bash scripts/install-wasm-bindgen.sh - name: Install binaryen (wasm-opt) run: sudo ./scripts/install-binaryen.sh @@ -163,6 +172,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -228,6 +239,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -240,7 +253,10 @@ jobs: # `sqlite3` isn't on PATH). - name: Install host sqlite3 for differential tests run: | - which sqlite3 || sudo apt-get update && sudo apt-get install -y sqlite3 + if ! command -v sqlite3 >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y sqlite3 + fi sqlite3 --version # Install the uutils multicall binary for @@ -302,6 +318,8 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -326,6 +344,8 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -354,6 +374,8 @@ jobs: # workflow_call events where pull_request context is absent. steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 @@ -423,8 +445,8 @@ jobs: # or moved suite would report green having run nothing. Assert the # files exist first. run: | - ls *.test.js - node --test *.test.js + ls ./*.test.js + node --test ./*.test.js # Detect whether DOPPLER_TOKEN is configured. Only set on push to # main (trusted code); PRs — including same-repo branches — must @@ -432,49 +454,67 @@ jobs: # `env.DOPPLER_AVAILABLE` so this job stays green when unconfigured. - name: Detect Doppler availability (trusted runs only) if: github.event_name == 'push' - env: - DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} - run: | - if [ -n "$DOPPLER_TOKEN" ]; then - echo "DOPPLER_AVAILABLE=true" >> "$GITHUB_ENV" - fi + run: echo "DOPPLER_AVAILABLE=${{ secrets.DOPPLER_TOKEN != '' }}" >> "$GITHUB_ENV" - name: Install Doppler CLI if: github.event_name == 'push' && env.DOPPLER_AVAILABLE == 'true' + uses: dopplerhq/cli-action@4819d808ab99e5cde19a0637a16536a4038fad73 # v4 + + - name: Build LLM examples without credentials + run: | + cargo build --example agent_tool --features http_client + cargo build -p bashkit-cli --features realfs --quiet + + # Resolve only the required key; this token-bearing process exits before + # repository code runs. Mask the output before crossing the step boundary. + - name: Fetch Anthropic example key + id: anthropic + continue-on-error: true + if: github.event_name == 'push' && env.DOPPLER_AVAILABLE == 'true' + shell: bash env: DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} - uses: dopplerhq/cli-action@4819d808ab99e5cde19a0637a16536a4038fad73 # v4 + run: | + key=$(doppler secrets get ANTHROPIC_API_KEY --plain) + printf '::add-mask::%s\n' "$key" + printf 'ANTHROPIC_API_KEY=%s\n' "$key" >> "$GITHUB_OUTPUT" - # External API dependency — don't block CI on Anthropic outages. - # ANTHROPIC_API_KEY is sourced from Doppler (single source of truth - # for non-GitHub secrets), so this step also requires Doppler to be - # available, which already implies a push to main. - # `--only-secrets` keeps the principle of least privilege: the - # step sees ANTHROPIC_API_KEY only, not every secret in Doppler. + # External API availability must not block ordinary repository checks. - name: Run LLM agent example - if: github.event_name == 'push' && env.DOPPLER_AVAILABLE == 'true' + if: github.event_name == 'push' && env.DOPPLER_AVAILABLE == 'true' && steps.anthropic.outcome == 'success' continue-on-error: true env: - DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} - run: doppler run --only-secrets ANTHROPIC_API_KEY -- cargo run --example agent_tool --features http_client + ANTHROPIC_API_KEY: ${{ steps.anthropic.outputs.ANTHROPIC_API_KEY }} + run: target/debug/examples/agent_tool - # External API dependency — same rationale as the LLM agent example - # above: an OpenAI outage, quota exhaustion, or billing lapse is not a - # bashkit regression and must not turn main red. - - name: Run harness OpenAI joke example - if: github.event_name == 'push' && env.DOPPLER_AVAILABLE == 'true' + # Resolve only the required key; this token-bearing process exits before + # repository code runs. Mask the output before crossing the step boundary. + - name: Fetch OpenAI example key + id: openai continue-on-error: true + if: github.event_name == 'push' && env.DOPPLER_AVAILABLE == 'true' + shell: bash env: DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} run: | - cargo build -p bashkit-cli --features realfs --quiet - doppler run --only-secrets OPENAI_API_KEY -- bash examples/harness-openai-joke.sh + key=$(doppler secrets get OPENAI_API_KEY --plain) + printf '::add-mask::%s\n' "$key" + printf 'OPENAI_API_KEY=%s\n' "$key" >> "$GITHUB_OUTPUT" + + - name: Run harness OpenAI joke example + if: github.event_name == 'push' && env.DOPPLER_AVAILABLE == 'true' && steps.openai.outcome == 'success' + continue-on-error: true + env: + OPENAI_API_KEY: ${{ steps.openai.outputs.OPENAI_API_KEY }} + run: bash examples/harness-openai-joke.sh fuzz-check: name: Fuzz Compile Check runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install Rust nightly uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly @@ -493,7 +533,7 @@ jobs: check: name: Check if: always() - needs: [lint, audit, test, windows-containment, test-windows, examples, fuzz-check] + needs: [lint, audit, test, windows-containment, test-windows, examples, fuzz-check, wasm, wasm-component, wasm-web] runs-on: ubuntu-latest steps: - name: Verify all jobs passed @@ -504,7 +544,10 @@ jobs: [[ "${{ needs.windows-containment.result }}" != "success" ]] || \ [[ "${{ needs.test-windows.result }}" != "success" ]] || \ [[ "${{ needs.examples.result }}" != "success" ]] || \ - [[ "${{ needs.fuzz-check.result }}" != "success" ]]; then + [[ "${{ needs.fuzz-check.result }}" != "success" ]] || \ + [[ "${{ needs.wasm.result }}" != "success" ]] || \ + [[ "${{ needs.wasm-component.result }}" != "success" ]] || \ + [[ "${{ needs.wasm-web.result }}" != "success" ]]; then echo "One or more required jobs failed" exit 1 fi diff --git a/.github/workflows/cli-binaries.yml b/.github/workflows/cli-binaries.yml index 5c0812e24..49fd58969 100644 --- a/.github/workflows/cli-binaries.yml +++ b/.github/workflows/cli-binaries.yml @@ -1,3 +1,4 @@ +# THREAT[TM-INF-027]: build only the immutable SHA exported by tag validation. # Prebuilt CLI binaries — dispatched by release.yml after GitHub Release is created. # GITHUB_TOKEN-created releases don't trigger `release: published` (anti-recursion). # The Release workflow dispatches this workflow explicitly, same pattern as everruns/everruns. @@ -22,6 +23,7 @@ jobs: contents: read outputs: tag: ${{ steps.validate.outputs.tag }} + sha: ${{ steps.source.outputs.sha }} steps: - name: Validate workflow input id: validate @@ -40,19 +42,22 @@ jobs: fetch-depth: 0 - name: Verify tag source + id: source env: RELEASE_TAG: ${{ steps.validate.outputs.tag }} run: | TAG="$RELEASE_TAG" + + git fetch --force origin main:refs/remotes/origin/main "refs/tags/$TAG:refs/tags/$TAG" + TAG_SHA=$(git rev-list -n 1 "$TAG") + # Validate and build this exact commit even if the tag later moves. + git checkout --detach "$TAG_SHA" VERSION="${TAG#v}" CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') if [ "$VERSION" != "$CARGO_VERSION" ]; then echo "Error: tag $TAG does not match Cargo.toml version $CARGO_VERSION" >&2 exit 1 fi - - git fetch --force origin main:refs/remotes/origin/main "refs/tags/$TAG:refs/tags/$TAG" - TAG_SHA=$(git rev-list -n 1 "$TAG") if [ "$TAG_SHA" != "$GITHUB_SHA" ]; then echo "Error: workflow ref $GITHUB_SHA does not match tag $TAG at $TAG_SHA" >&2 exit 1 @@ -62,6 +67,7 @@ jobs: exit 1 fi echo "Release tag $TAG verified at $TAG_SHA and reachable from origin/main" + echo "sha=$TAG_SHA" >> "$GITHUB_OUTPUT" build: name: Build CLI (${{ matrix.target }}) @@ -86,7 +92,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - ref: ${{ needs.validate-tag.outputs.tag }} + ref: ${{ needs.validate-tag.outputs.sha }} persist-credentials: false - name: Install Rust toolchain diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 1e1ba10a7..e24f7b30c 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -216,22 +216,29 @@ jobs: - name: Install Doppler CLI if: steps.doppler.outputs.available == 'true' uses: dopplerhq/cli-action@4819d808ab99e5cde19a0637a16536a4038fad73 # v4 + + # Resolve only the required key; this token-bearing process exits before + # repository code runs. Mask the output before crossing the step boundary. + - name: Fetch OpenAI example key + id: openai + if: steps.doppler.outputs.available == 'true' + shell: bash env: DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} + run: | + key=$(doppler secrets get OPENAI_API_KEY --plain) + printf '::add-mask::%s\n' "$key" + printf 'OPENAI_API_KEY=%s\n' "$key" >> "$GITHUB_OUTPUT" - # `--only-secrets OPENAI_API_KEY` keeps the runtime env scoped to - # just the key these scripts need, instead of injecting every - # secret in the Doppler config. DOPPLER_TOKEN stays scoped to this - # trusted secret-fetch step, not npm install/build/test/example code. - name: Run AI examples if: steps.doppler.outputs.available == 'true' working-directory: examples env: - DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} + OPENAI_API_KEY: ${{ steps.openai.outputs.OPENAI_API_KEY }} run: | - doppler run --only-secrets OPENAI_API_KEY -- ${{ matrix.run }} openai_tool.mjs - doppler run --only-secrets OPENAI_API_KEY -- ${{ matrix.run }} vercel_ai_tool.mjs - doppler run --only-secrets OPENAI_API_KEY -- ${{ matrix.run }} langchain_agent.mjs + ${{ matrix.run }} openai_tool.mjs + ${{ matrix.run }} vercel_ai_tool.mjs + ${{ matrix.run }} langchain_agent.mjs # Gate job for branch protection js-check: diff --git a/.github/workflows/publish-js.yml b/.github/workflows/publish-js.yml index ff109bd36..95d9f6530 100644 --- a/.github/workflows/publish-js.yml +++ b/.github/workflows/publish-js.yml @@ -220,31 +220,27 @@ jobs: run: node --test __test__/runtime-compat/*.test.mjs working-directory: crates/bashkit-js - - name: Run examples + # Use the same reviewed dependency graph as CI; test the downloaded + # release binding after installation, never a freshly resolved npm build. + - name: Install example dependencies and link local build shell: bash + working-directory: examples run: | + pnpm install --frozen-lockfile --ignore-scripts + rm -rf node_modules/@everruns/bashkit mkdir -p node_modules/@everruns if [ "$RUNNER_OS" = "Windows" ]; then - cp -r crates/bashkit-js node_modules/@everruns/bashkit + cp -r ../crates/bashkit-js node_modules/@everruns/bashkit else ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit fi - node examples/bash_basics.mjs - node examples/data_pipeline.mjs - node examples/llm_tool.mjs - - name: Install AI SDK dependencies and re-link + - name: Run examples shell: bash run: | - echo '{"type":"module","private":true}' > package.json - pnpm add --ignore-scripts openai ai @ai-sdk/openai @langchain/core @langchain/langgraph @langchain/openai zod - rm -rf node_modules/@everruns/bashkit - mkdir -p node_modules/@everruns - if [ "$RUNNER_OS" = "Windows" ]; then - cp -r crates/bashkit-js node_modules/@everruns/bashkit - else - ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit - fi + node examples/bash_basics.mjs + node examples/data_pipeline.mjs + node examples/llm_tool.mjs - name: Detect Doppler availability id: doppler @@ -253,26 +249,33 @@ jobs: - name: Install Doppler CLI if: steps.doppler.outputs.available == 'true' uses: dopplerhq/cli-action@4819d808ab99e5cde19a0637a16536a4038fad73 # v4 - env: - DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} - # `--only-secrets OPENAI_API_KEY` keeps the runtime env scoped to - # just the key these scripts need, instead of injecting every - # secret in the Doppler config. DOPPLER_TOKEN stays scoped to this - # trusted secret-fetch step, not npm install/build/test/example code. # Skipped on Windows + Node 24: libuv hits an assertion failure # (`!(handle->flags & UV_HANDLE_CLOSING)` in src/win/async.c) during # process shutdown of the example scripts on that combination; the # same examples pass on every other platform/Node combination. - - name: Run AI examples + # Resolve only the required key; this token-bearing process exits before + # repository code runs. Mask the output before crossing the step boundary. + - name: Fetch OpenAI example key + id: openai if: steps.doppler.outputs.available == 'true' && !(runner.os == 'Windows' && matrix.node == '24') shell: bash env: DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} run: | - doppler run --only-secrets OPENAI_API_KEY -- node examples/openai_tool.mjs - doppler run --only-secrets OPENAI_API_KEY -- node examples/vercel_ai_tool.mjs - doppler run --only-secrets OPENAI_API_KEY -- node examples/langchain_agent.mjs + key=$(doppler secrets get OPENAI_API_KEY --plain) + printf '::add-mask::%s\n' "$key" + printf 'OPENAI_API_KEY=%s\n' "$key" >> "$GITHUB_OUTPUT" + + - name: Run AI examples + if: steps.doppler.outputs.available == 'true' && !(runner.os == 'Windows' && matrix.node == '24') + shell: bash + env: + OPENAI_API_KEY: ${{ steps.openai.outputs.OPENAI_API_KEY }} + run: | + node examples/openai_tool.mjs + node examples/vercel_ai_tool.mjs + node examples/langchain_agent.mjs # ============================================================================ # Test bindings on Linux @@ -312,14 +315,14 @@ jobs: } else { console.log('PLATFORM=linux/amd64') } - " >> $GITHUB_OUTPUT + " >> "$GITHUB_OUTPUT" node -e " if ('${{ matrix.target }}'.endsWith('-musl')) { console.log('IMAGE=node:${{ matrix.node }}-alpine') } else { console.log('IMAGE=node:${{ matrix.node }}-slim') } - " >> $GITHUB_OUTPUT + " >> "$GITHUB_OUTPUT" - name: Install dependencies run: pnpm install --frozen-lockfile @@ -357,8 +360,12 @@ jobs: ${{ steps.docker.outputs.IMAGE }} sh -c "node --test __test__/runtime-compat/*.test.mjs" - - name: Link package for examples + # Match CI's committed dependency graph, then select the release artifact. + - name: Install example dependencies and link local build + working-directory: examples run: | + pnpm install --frozen-lockfile --ignore-scripts + rm -rf node_modules/@everruns/bashkit mkdir -p node_modules/@everruns ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit @@ -370,14 +377,6 @@ jobs: ${{ steps.docker.outputs.IMAGE }} sh -c "node examples/bash_basics.mjs && node examples/data_pipeline.mjs && node examples/llm_tool.mjs" - - name: Install AI SDK dependencies and re-link - run: | - echo '{"type":"module","private":true}' > package.json - pnpm add --ignore-scripts openai ai @ai-sdk/openai @langchain/core @langchain/langgraph @langchain/openai zod - rm -rf node_modules/@everruns/bashkit - mkdir -p node_modules/@everruns - ln -s ${{ github.workspace }}/crates/bashkit-js node_modules/@everruns/bashkit - - name: Detect Doppler availability id: doppler run: echo "available=${{ secrets.DOPPLER_TOKEN != '' }}" >> "$GITHUB_OUTPUT" @@ -385,17 +384,28 @@ jobs: - name: Install Doppler CLI if: steps.doppler.outputs.available == 'true' uses: dopplerhq/cli-action@4819d808ab99e5cde19a0637a16536a4038fad73 # v4 + + # Resolve only the required key; this token-bearing process exits before + # repository code runs. Mask the output before crossing the step boundary. + - name: Fetch OpenAI example key + id: openai + if: steps.doppler.outputs.available == 'true' + shell: bash env: DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} + run: | + key=$(doppler secrets get OPENAI_API_KEY --plain) + printf '::add-mask::%s\n' "$key" + printf 'OPENAI_API_KEY=%s\n' "$key" >> "$GITHUB_OUTPUT" - name: Run AI examples if: steps.doppler.outputs.available == 'true' env: - DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} + OPENAI_API_KEY: ${{ steps.openai.outputs.OPENAI_API_KEY }} run: > docker run --rm -v ${{ github.workspace }}:${{ github.workspace }} -w ${{ github.workspace }} - -e OPENAI_API_KEY=$(doppler secrets get OPENAI_API_KEY --plain) + -e OPENAI_API_KEY --platform ${{ steps.docker.outputs.PLATFORM }} ${{ steps.docker.outputs.IMAGE }} sh -c "node examples/openai_tool.mjs && node examples/vercel_ai_tool.mjs && node examples/langchain_agent.mjs" diff --git a/.github/workflows/publish-wasm.yml b/.github/workflows/publish-wasm.yml index 79e79a1ec..cb7fdbac9 100644 --- a/.github/workflows/publish-wasm.yml +++ b/.github/workflows/publish-wasm.yml @@ -6,8 +6,7 @@ # - NPM_TOKEN secret with publish access # - id-token: write permission for npm provenance (OIDC attestation) # -# Keep WASM_BINDGEN_VERSION in lockstep with the wasm-bindgen version in -# Cargo.lock — a CLI/crate mismatch fails the bindgen step. +# Derive the bindgen CLI from Cargo.lock; never duplicate its schema version. name: Publish Web @@ -21,7 +20,6 @@ permissions: env: CARGO_TERM_COLOR: always - WASM_BINDGEN_VERSION: "0.2.126" jobs: verify-source: @@ -59,7 +57,7 @@ jobs: - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 - name: Install wasm-bindgen-cli - run: cargo install wasm-bindgen-cli --version "$WASM_BINDGEN_VERSION" --locked + run: bash scripts/install-wasm-bindgen.sh # Pinned + checksummed, because this job's output is what ships to npm. # See scripts/install-binaryen.sh. diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3724077d7..2ba4ee533 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,3 +1,4 @@ +# Verify packages without registry credentials; upload skips compilation. name: Publish on: @@ -58,8 +59,11 @@ jobs: fi echo "Version verified: $TAG_VERSION" + - name: Verify bashkit package without credentials + run: cargo publish --dry-run -p bashkit + - name: Publish bashkit to crates.io - run: cargo publish -p bashkit + run: cargo publish --no-verify -p bashkit env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} @@ -80,8 +84,11 @@ jobs: - name: Wait for crates.io index update run: sleep 30 + - name: Verify bashkit-cli package without credentials + run: cargo publish --dry-run -p bashkit-cli + - name: Publish bashkit-cli to crates.io - run: cargo publish -p bashkit-cli + run: cargo publish --no-verify -p bashkit-cli env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 10b6c4efd..982d80a13 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -99,6 +99,10 @@ jobs: pip install bashkit --no-index --find-links crates/bashkit-python/dist --force-reinstall pip install pytest pytest-asyncio langchain-core langgraph fastapi httpx maturin + - name: Install Deep Agents integration dependencies + if: matrix.python-version != '3.9' && matrix.python-version != '3.10' + run: pip install 'bashkit[deepagents]' --find-links crates/bashkit-python/dist + # random-fs is a separate, non-abi3 pyo3 extension (own workspace, no # abi3-py39), so it must be built against each interpreter. - name: Build random filesystem fixture diff --git a/AGENTS.md b/AGENTS.md index 79262fd09..afb1ba824 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -106,6 +106,13 @@ export GH_TOKEN=$(curl -s "https://api.doppler.com/v3/configs/config/secret?name -u "$DOPPLER_TOKEN:" | python3 -c "import sys,json; print(json.load(sys.stdin)['value']['raw'])") ``` +### Maintenance invocation + +"Run maintenance" (including "maintainace" / "maintaiance") means analyze, fix, +validate, and ship through green CI and squash-merge. Use the maintain and ship +skills. Do not stop at a report, local commit, or deferral issues. Analysis-only +requires an explicit request. Preserve security contracts and never merge red CI. + ### Local Dev ```bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 08f319418..a50c9c806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ ## [Unreleased] +### Fixed + +- Host calls enforce execution deadlines while parked. +- Runtime mounts use canonical replay keys across binding rebuilds. +- SQLite budgets account for work within a single engine step. +- Documentation preserves inline SVG diagrams during Markdown rendering. +- CI's aggregate check includes WASM validation, and secret-backed examples + fetch scoped API keys in separate steps that end before repository code runs. +- Browser persistence preserves the previous save when directory traversal fails. +- BashTool snapshot constructors retain supplied JavaScript custom builtins. +- Release binary builds use the exact validated commit; Cargo verification runs + without registry credentials, and JS release examples use reviewed lockfiles. +- CI drops unused write permissions and installs SQLite only when missing. +- Deep Agents supports the current structured backend protocol, exact VFS file + transfers, grep glob filters, and execution truncation metadata. +- Anthropic sanitized tool output is capped after XML escaping, preserving + complete entities and matching the OpenAI adapter. + +### Changed + +- Refresh Rust, JavaScript, site, and example dependencies; update DeepSec to + 2.3.9. Pin Monty to 0.0.19 and get-size2 to 0.10.1 to preserve per-VM + memory, work, and cancellation limits. +- Maintenance requests now run analysis, fixes, validation, and shipping through + green CI and merge, including common misspellings. +- Refresh benchmark baselines; select a supported Bash from `PATH` and record + its version instead of silently using macOS's obsolete system shell. + ## [0.17.1] - 2026-08-22 ### Highlights diff --git a/Cargo.lock b/Cargo.lock index 2dbe39b2b..2b48e17a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,13 +51,13 @@ dependencies = [ [[package]] name = "aes" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58" +checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32" dependencies = [ "cipher 0.5.2", "cpubits", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "zeroize", ] @@ -77,16 +77,16 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf011db2e21ce0d575593d749db5554b47fed37aff429e4dc50bc91ac93a028" +checksum = "7f2b8006a0c83f52b62ba44a97b58bf76fe2f70a329e588f67f89691d93d498f" dependencies = [ "aead 0.6.1", - "aes 0.9.2", + "aes 0.9.3", "cipher 0.5.2", "ctr 0.10.1", + "ctutils", "ghash 0.6.0", - "subtle", "zeroize", ] @@ -157,15 +157,15 @@ checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "antithesis_sdk" -version = "0.2.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08410fcac93669a476c006cd6c4512ac1e2b30fd117231a5d55d8a2c76599b82" +checksum = "a8ed6751597568a284314fd9e2db420665898600109dedc7d8d9b588f00ae9c4" dependencies = [ "libc", "libloading 0.8.9", "linkme", "once_cell", - "rand 0.8.7", + "rand 0.8.8", "rustc_version_runtime", "serde", "serde_json", @@ -188,13 +188,13 @@ dependencies = [ [[package]] name = "argon2" -version = "0.6.0-rc.8" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5" +checksum = "134c52ddac6d63c576bef8168db10c83c49c26444ecbc68060fef078925a901c" dependencies = [ "base64ct", "blake2", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "password-hash", ] @@ -238,7 +238,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -294,9 +294,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.18.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -305,9 +305,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.44.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" dependencies = [ "cc", "cmake", @@ -360,6 +360,7 @@ dependencies = [ "flate2", "futures-core", "futures-util", + "get-size2", "getrandom 0.4.3", "gloo-timers", "hmac", @@ -449,7 +450,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 3.0.4", + "syn 3.0.5", "tempfile", "toml", ] @@ -488,7 +489,7 @@ name = "bashkit-python" version = "0.17.1" dependencies = [ "bashkit", - "num-bigint", + "num-bigint 0.4.8", "pyo3", "pyo3-async-runtimes", "serde_json", @@ -532,7 +533,7 @@ checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695" dependencies = [ "autocfg", "libm", - "num-bigint", + "num-bigint 0.4.8", "num-integer", "num-traits", ] @@ -578,9 +579,9 @@ dependencies = [ [[package]] name = "blake2" -version = "0.11.0-rc.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690" +checksum = "5b5d4d889834ee8ecfc0f8426ad30faf7cdcb10f741a8e6d7224d95325479f6f" dependencies = [ "digest", ] @@ -616,9 +617,9 @@ dependencies = [ [[package]] name = "branches" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fba76cd916045514e3064707df9ed5282b94419444c2753a6c62ecaf458e56" +checksum = "7436b21fd6195415058eb41c9090e156b556bc7e0377bb57c5c026a421521525" dependencies = [ "rustc_version", ] @@ -663,7 +664,7 @@ checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -713,9 +714,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.3" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" +checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" dependencies = [ "find-msvc-tools", "jobserver", @@ -749,7 +750,7 @@ checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", "cipher 0.5.2", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "rand_core 0.10.1", "zeroize", ] @@ -856,7 +857,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -915,9 +916,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.7" +version = "4.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" dependencies = [ "bytes", "memchr", @@ -1021,9 +1022,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" dependencies = [ "libc", ] @@ -1039,9 +1040,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", ] @@ -1209,7 +1210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "curve25519-dalek-derive", "digest", "fiat-crypto", @@ -1280,9 +1281,9 @@ dependencies = [ [[package]] name = "der" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" +checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a" dependencies = [ "const-oid", "pem-rfc7468", @@ -1335,7 +1336,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -1399,9 +1400,9 @@ dependencies = [ [[package]] name = "either" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "elliptic-curve" @@ -1491,7 +1492,7 @@ checksum = "fe5e43d0f78a42ad591453aedb1d7ae631ce7ee445c7643691055a9ed8d3b01c" dependencies = [ "log", "once_cell", - "rand 0.8.7", + "rand 0.8.8", ] [[package]] @@ -1557,9 +1558,9 @@ checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" [[package]] name = "flate2" @@ -1667,7 +1668,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -1827,6 +1828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5" dependencies = [ "polyval 0.7.3", + "zeroize", ] [[package]] @@ -1911,9 +1913,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" [[package]] name = "hex" @@ -2013,9 +2015,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" dependencies = [ "atomic-waker", "bytes", @@ -2095,9 +2097,9 @@ dependencies = [ [[package]] name = "icu_collator" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59eea194d189ca897bcb960fd2130f9e7d53998460d4d32e85a60b10d5507cf4" +checksum = "08984ed58ac439ebf3e13d2cf26b0c46a60afcd21721c1d14087c0b240344dda" dependencies = [ "icu_collator_data", "icu_collections", @@ -2134,9 +2136,9 @@ dependencies = [ [[package]] name = "icu_locale" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250ec6d4ac288584d1d0ba29dbfad7737635b7b0e822441f4026a523e3fdb147" +checksum = "785f595c61ef57169a467eeed7b4b6936a66f6cacbb116a96ee86da983251bf4" dependencies = [ "icu_collections", "icu_locale_core", @@ -2233,9 +2235,9 @@ checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -2267,9 +2269,9 @@ checksum = "cfdf4f5d937a025381f5ab13624b1c5f51414bfe5c9885663226eae8d6d39560" [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -2308,18 +2310,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "internal-russh-num-bigint" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8e22120c32fb4d19ec55fba35015f57095cd95a2e3b732e44457f5915b2ee8" -dependencies = [ - "num-integer", - "num-traits", - "rand 0.10.2", - "rand_core 0.10.1", -] - [[package]] name = "interpolator" version = "0.5.0" @@ -2352,9 +2342,9 @@ checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is-macro" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +checksum = "8267aa6001e25494f3015f9663bbd88a18240c74483afa5f0934a1b3e4c388e9" dependencies = [ "heck", "proc-macro2", @@ -2397,9 +2387,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jaq-core" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7561783b20275a6c9cb576e39208b0c635f34ef14357f1f05a2927a774f3adec" +checksum = "f72ba7158fe9fc1bc4baace1b71e6d0d3d27c6839dd87dc1f233dd408f5dd85b" dependencies = [ "dyn-clone", "once_cell", @@ -2408,9 +2398,9 @@ dependencies = [ [[package]] name = "jaq-json" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d801b0b57f10064c4e9f5a4f6c97d0ccf62649b179ff8ac23cd494a3120ee9" +checksum = "4be2f53207ffb1313a2d2ad819cfa8bfaa7936cb85a110eda10fd5da822cec94" dependencies = [ "bstr", "bytes", @@ -2419,7 +2409,7 @@ dependencies = [ "indexmap", "jaq-core", "jaq-std", - "num-bigint", + "num-bigint 0.4.8", "num-traits", "ryu", "self_cell", @@ -2427,9 +2417,9 @@ dependencies = [ [[package]] name = "jaq-std" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7941c8de9c591052050550f228c62ef80d3ecbd84c330f5c454bc8ebb7a04089" +checksum = "9f90e485abfd3189510c996e77be55056a51038b0fa5805e6c97b5dcf0d9ab55" dependencies = [ "aho-corasick", "base64 0.22.1", @@ -2503,7 +2493,7 @@ dependencies = [ "ahash", "bitvec", "lexical-parse-float", - "num-bigint", + "num-bigint 0.4.8", "num-traits", "pyo3", "smallvec", @@ -2570,9 +2560,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.104" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" dependencies = [ "cfg-if", "futures-util", @@ -2581,12 +2571,12 @@ dependencies = [ [[package]] name = "keccak" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd9697dc4a9a62e2da93389f34400b77a28f0287711263cabb203b3ccb9c0e4" +checksum = "d8f198d1db720e4940b5a493201d199d9f24f568f8f746bd13706243a2f71598" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", ] [[package]] @@ -2685,7 +2675,7 @@ checksum = "77060ebe535362c3da75682cd17b0431017b6e7c5661e714fc69a7ad017d1301" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -2711,9 +2701,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "loom" @@ -2825,9 +2815,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" dependencies = [ "libc", "wasi", @@ -2902,7 +2892,7 @@ dependencies = [ "libm", "monty-macros", "monty-types", - "num-bigint", + "num-bigint 0.4.8", "num-integer", "num-traits", "postcard", @@ -2938,7 +2928,7 @@ checksum = "3a8988fa8d9902432d5e685a7bb97c84844022dd9560e796c00b119a4c27519b" dependencies = [ "chrono", "monty-macros", - "num-bigint", + "num-bigint 0.4.8", "num-traits", "serde", "strum 0.27.2", @@ -3058,6 +3048,18 @@ dependencies = [ "serde", ] +[[package]] +name = "num-bigint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0" +dependencies = [ + "num-integer", + "num-traits", + "rand 0.10.2", + "rand_core 0.10.1", +] + [[package]] name = "num-integer" version = "0.1.47" @@ -3102,9 +3104,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "ordermap" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7476a5b122ff1fce7208e7ee9dccd0a516e835f5b8b19b8f3c98a34cf757c1" +checksum = "16a756ddcdeb6c0efc8927bdf0ed112885815a35ba350472a467be5104f01ca1" dependencies = [ "indexmap", ] @@ -3126,9 +3128,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "owo-colors" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" +checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8" [[package]] name = "oxc-miette" @@ -3137,7 +3139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4356a61f2ed4c9b3610245215fbf48970eb277126919f87db9d0efa93a74245c" dependencies = [ "cfg-if", - "owo-colors 4.3.0", + "owo-colors 4.4.0", "oxc-miette-derive", "textwrap", "thiserror", @@ -3221,7 +3223,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71b23b64fa8c4a84b1406de383c4666366c9f54ffb9cb11a63b8d7433950460a" dependencies = [ "cow-utils", - "num-bigint", + "num-bigint 0.4.8", "num-traits", "oxc_allocator", "oxc_ast", @@ -3255,7 +3257,7 @@ dependencies = [ "bitflags 2.13.1", "cow-utils", "memchr", - "num-bigint", + "num-bigint 0.4.8", "num-traits", "oxc_allocator", "oxc_ast", @@ -3392,9 +3394,9 @@ dependencies = [ [[package]] name = "pageant" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3adadc44070da6f464b0918655a12f5792c156e088d8c4082d13e27d94c3e791" +checksum = "6d8eab09a361a4afe0b1668be978cd97e4f052e927a92b0b608cf902965d49ce" dependencies = [ "base16ct", "byteorder", @@ -3540,7 +3542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.7", + "rand 0.8.8", ] [[package]] @@ -3615,8 +3617,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63d440a804ec8d6fafbb6b84471e013286658d373248927692ab3366686220ca" dependencies = [ - "aes 0.9.2", - "aes-gcm 0.11.0", + "aes 0.9.3", + "aes-gcm 0.11.1", "cbc", "der", "pbkdf2", @@ -3692,7 +3694,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c" dependencies = [ - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "universal-hash 0.6.1", "zeroize", ] @@ -3716,8 +3718,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd" dependencies = [ "cpubits", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "universal-hash 0.6.1", + "zeroize", ] [[package]] @@ -3728,9 +3731,9 @@ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" dependencies = [ "portable-atomic", ] @@ -3785,7 +3788,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" dependencies = [ "proc-macro2", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -3883,7 +3886,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b" dependencies = [ "libc", - "num-bigint", + "num-bigint 0.4.8", "num-traits", "once_cell", "portable-atomic", @@ -4016,9 +4019,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4359,11 +4362,11 @@ dependencies = [ [[package]] name = "russh" -version = "0.63.1" +version = "0.63.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bab1b87d915817d5d9cc352637cd40d5f0b298a48c6309af9156a4addc3031" +checksum = "8e134e2480f4e86f83e4aa45b4c0a9723f84beaffa694c54bdf056e74efdd7dd" dependencies = [ - "aes 0.9.2", + "aes 0.9.3", "aws-lc-rs", "bitflags 2.13.1", "block-padding", @@ -4390,13 +4393,12 @@ dependencies = [ "hex-literal", "hmac", "inout 0.2.2", - "internal-russh-num-bigint", "keccak", "log", "md5", "ml-kem", "module-lattice", - "num-bigint", + "num-bigint 0.5.1", "p256", "p384", "p521", @@ -4557,9 +4559,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.14" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -4766,7 +4768,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -4836,7 +4838,7 @@ checksum = "a22144e767da4ddd8416dbf383700542ffd8a5dc493dfecedfe1fe3ad03c98ae" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -4846,7 +4848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "digest", ] @@ -4863,7 +4865,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "digest", ] @@ -4915,7 +4917,7 @@ dependencies = [ "generator", "hex", "owo-colors 3.5.0", - "rand 0.8.7", + "rand 0.8.8", "rand_core 0.6.4", "rand_pcg", "scoped-tls", @@ -5004,9 +5006,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.2" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" dependencies = [ "serde", ] @@ -5076,8 +5078,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d801accda99469cde6d73da741422610fdf6508a72d9a69d1b55cb241c720597" dependencies = [ "aead 0.6.1", - "aes 0.9.2", - "aes-gcm 0.11.0", + "aes 0.9.3", + "aes-gcm 0.11.1", "chacha20", "cipher 0.5.2", "ctutils", @@ -5202,9 +5204,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" dependencies = [ "proc-macro2", "quote", @@ -5333,7 +5335,7 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -5368,9 +5370,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b" dependencies = [ "tinyvec_macros", ] @@ -5405,14 +5407,14 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] name = "tokio-rustls" -version = "0.26.4" +version = "0.26.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" dependencies = [ "rustls", "tokio", @@ -5455,9 +5457,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.4+spec-1.1.0" +version = "1.1.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" +checksum = "12c0ba9680044b4ce98d391a62094047eada0d64860b80166c39f4a6b5640785" dependencies = [ "indexmap", "serde_core", @@ -5606,9 +5608,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "turso_core" -version = "0.8.0-pre.7" +version = "0.8.0-pre.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae723f6e4b3271dfa3d3e11dedab5b9146c278409fe948bdfccc2658af8a09eb" +checksum = "8aa9df0e2dd9894ddc61cf6c35e0726f4fc26dd08c584afebd319c1969aaf504" dependencies = [ "aegis", "aes 0.8.4", @@ -5642,7 +5644,7 @@ dependencies = [ "libm", "loom", "miette", - "num-bigint", + "num-bigint 0.4.8", "num-traits", "pack1", "parking_lot", @@ -5678,9 +5680,9 @@ dependencies = [ [[package]] name = "turso_ext" -version = "0.8.0-pre.7" +version = "0.8.0-pre.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9704b1b4b5dc8dbdc56fd4c3a44fb7be47d91e9d240cada262a1d6f55b0fc08" +checksum = "a27e351e5f30b21d186a62895761a59a944e209111ea7ba84bf8c611068d87fc" dependencies = [ "chrono", "getrandom 0.4.3", @@ -5689,9 +5691,9 @@ dependencies = [ [[package]] name = "turso_macros" -version = "0.8.0-pre.7" +version = "0.8.0-pre.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c72bcec01a1828c7494cd7f12a7d6749897eb579681bc1de401b4a6c7f3c1d" +checksum = "f5c30032b9cd45798d5ed012a3728d9cf4e39a8dbb7d820d82908facb4b665dc" dependencies = [ "proc-macro2", "quote", @@ -5700,9 +5702,9 @@ dependencies = [ [[package]] name = "turso_parser" -version = "0.8.0-pre.7" +version = "0.8.0-pre.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e32af53f89c67f2d202e25ea9c4913f596e85fcddaf587295ae8e1f404599960" +checksum = "6823c91624d12ae67d6c6dfc819406a279265ca8879307f92c12d7de77bc6846" dependencies = [ "bitflags 2.13.1", "memchr", @@ -5715,9 +5717,9 @@ dependencies = [ [[package]] name = "twox-hash" -version = "2.1.3" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8464ec13c3691491391d9fce00f6416c9a48e46972f72d7865688be2080192c9" +checksum = "5283634e518fe9e82c7b20520bb4bc209009fd16c82077c802f8111ecbb0117a" dependencies = [ "rand 0.10.2", ] @@ -5819,7 +5821,7 @@ dependencies = [ "getopts", "log", "phf_codegen", - "rand 0.8.7", + "rand 0.8.8", ] [[package]] @@ -5904,9 +5906,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.24.1" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cefc03fd367c0c6d4305de1b312cf00248c4114f4a0418ce6a6af769e3b0bd9" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -5971,9 +5973,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" dependencies = [ "cfg-if", "once_cell", @@ -5984,9 +5986,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.77" +version = "0.4.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" +checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" dependencies = [ "js-sys", "wasm-bindgen", @@ -5994,9 +5996,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6004,22 +6006,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" dependencies = [ "unicode-ident", ] @@ -6039,9 +6041,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.104" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" +checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" dependencies = [ "js-sys", "wasm-bindgen", @@ -6303,13 +6305,14 @@ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "wnaf" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1" +checksum = "795ca18b3fdb5e62bf982199278341ddcf7ebf7d32e25e212ad05d496e95f6fa" dependencies = [ "ff", "group", "hybrid-array", + "primefield", ] [[package]] @@ -6440,9 +6443,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b5c6b5976d66c1d703c4fd17d3f5e43c8cedaacf604961b171adc7130896d8" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "serde", "yoke", @@ -6452,13 +6455,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47402523226a02bfe5230160dc3ccc089aa6f6f19e7fcbb4e6f824bbb1b4aa62" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] diff --git a/README.md b/README.md index 73b5a1725..3116743b0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Homepage: [bashkit.sh](https://bashkit.sh) - **Secure by default** - No process spawning, no filesystem access, no network access unless explicitly enabled. [280+ threats](knowledge/security/threat-model.md) analyzed and mitigated - **POSIX compliant** - Substantial IEEE 1003.1-2024 Shell Command Language compliance -- **Sandboxed, in-process execution** - All 164 commands reimplemented in Rust, no `fork`/`exec` +- **Sandboxed, in-process execution** - All 167 commands reimplemented in Rust, no `fork`/`exec` - **Virtual filesystem** - InMemoryFs, OverlayFs, MountableFs with optional RealFs backend (`realfs` feature) - **Resource limits** - Command count, loop iterations, function depth, output size, filesystem size, parser fuel - **Network allowlist** - HTTP access denied by default, per-domain control @@ -150,7 +150,7 @@ check must consult it. Available in Rust, Node (`bash.analyze()`), and Python -## Built-in Commands (164) +## Built-in Commands (167) | Category | Commands | |----------|----------| @@ -604,10 +604,10 @@ Bashkit is built for running untrusted scripts from AI agents and users. Securit | Layer | Protection | |-------|------------| -| **No process spawning** | All 164 commands are reimplemented in Rust, no `fork`, `exec`, or shell escape | +| **No process spawning** | All 167 commands are reimplemented in Rust, no `fork`, `exec`, or shell escape | | **Virtual filesystem** | Scripts see an in-memory FS by default; no host filesystem access unless explicitly mounted | | **Network allowlist** | HTTP access is denied by default; each domain must be explicitly allowed | -| **Resource limits** | Configurable caps on commands (10K), loop iterations (100K), function depth (100), output (10MB), input (10MB) | +| **Resource limits** | Configurable caps on commands (10K), loop iterations (10K per loop, 1M total), function depth (100), stdout/stderr (1MiB each), input (10MB) | | **Filesystem limits** | Max total bytes (100MB), max file size (10MB), max file count (10K), prevents zip bombs, tar bombs, and append floods | | **Parser limits** | Timeout (5s), fuel budget (100K ops), AST depth (100), prevents pathological input from hanging the interpreter | | **Multi-tenant isolation** | Each `Bash` instance is fully isolated, no shared state between tenants | diff --git a/crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.json b/crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.json new file mode 100644 index 000000000..b07176ec6 --- /dev/null +++ b/crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.json @@ -0,0 +1,4842 @@ +{ + "moniker": "mykhailosmac-macos-aarch64", + "timestamp": "1788629833", + "system": { + "hostname": "Mykhailos-Mac-mini.local", + "os": "macos", + "arch": "aarch64", + "cpus": 12, + "moniker": "mykhailosmac-macos-aarch64" + }, + "iterations": 10, + "warmup": 2, + "prewarm_cases": 3, + "runners": [ + "bashkit", + "bash" + ], + "runner_versions": { + "bash": "/opt/homebrew/bin/bash (GNU Bash 5.3.15(1)-release)" + }, + "results": [ + { + "runner": "bashkit", + "case_name": "startup_empty", + "category": "startup", + "iterations": 10, + "times_ns": [ + 22417, + 17917, + 59291, + 24458, + 18291, + 38834, + 24208, + 16542, + 16208, + 16125 + ], + "mean_ns": 25429.1, + "stddev_ns": 13022.235249372514, + "min_ns": 16125, + "max_ns": 59291, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "startup_empty", + "category": "startup", + "iterations": 10, + "times_ns": [ + 4693833, + 4215375, + 4450708, + 4478083, + 4660083, + 4902250, + 4444958, + 4439917, + 4004209, + 4224833 + ], + "mean_ns": 4451424.9, + "stddev_ns": 247688.12787029173, + "min_ns": 4004209, + "max_ns": 4902250, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "startup_true", + "category": "startup", + "iterations": 10, + "times_ns": [ + 17917, + 16416, + 15667, + 15584, + 15125, + 15000, + 15417, + 14792, + 14916, + 15000 + ], + "mean_ns": 15583.4, + "stddev_ns": 902.0071174885485, + "min_ns": 14792, + "max_ns": 17917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "startup_true", + "category": "startup", + "iterations": 10, + "times_ns": [ + 4313250, + 5364542, + 6039917, + 5910750, + 5894125, + 5545250, + 5095000, + 4536834, + 4333209, + 3824750 + ], + "mean_ns": 5085762.7, + "stddev_ns": 747528.2320021164, + "min_ns": 3824750, + "max_ns": 6039917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "startup_echo", + "category": "startup", + "iterations": 10, + "times_ns": [ + 18375, + 17250, + 20250, + 16959, + 16125, + 15666, + 15667, + 16083, + 15708, + 15834 + ], + "mean_ns": 16791.7, + "stddev_ns": 1423.9099725755136, + "min_ns": 15666, + "max_ns": 20250, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "startup_echo", + "category": "startup", + "iterations": 10, + "times_ns": [ + 4429292, + 6087750, + 10357666, + 6499125, + 5060375, + 5226667, + 4841000, + 5199084, + 7401542, + 6350333 + ], + "mean_ns": 6145283.4, + "stddev_ns": 1644836.4493472413, + "min_ns": 4429292, + "max_ns": 10357666, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "startup_exit", + "category": "startup", + "iterations": 10, + "times_ns": [ + 19042, + 16541, + 16084, + 16042, + 15708, + 15375, + 15542, + 15458, + 15625, + 16583 + ], + "mean_ns": 16200.0, + "stddev_ns": 1030.3007327960124, + "min_ns": 15375, + "max_ns": 19042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "startup_exit", + "category": "startup", + "iterations": 10, + "times_ns": [ + 4296792, + 4454459, + 4294167, + 4090041, + 4590416, + 4152459, + 3949209, + 3846209, + 4077500, + 4373459 + ], + "mean_ns": 4212471.1, + "stddev_ns": 219400.1977991132, + "min_ns": 3846209, + "max_ns": 4590416, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "var_assign_simple", + "category": "variables", + "iterations": 10, + "times_ns": [ + 20084, + 18834, + 20708, + 18417, + 17791, + 19417, + 17541, + 17167, + 18416, + 17125 + ], + "mean_ns": 18550.0, + "stddev_ns": 1158.2567072976526, + "min_ns": 17125, + "max_ns": 20708, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "var_assign_simple", + "category": "variables", + "iterations": 10, + "times_ns": [ + 4161375, + 4838542, + 4216042, + 4278625, + 4446250, + 4337041, + 4456166, + 4127209, + 3801125, + 4168250 + ], + "mean_ns": 4283062.5, + "stddev_ns": 256343.79244649166, + "min_ns": 3801125, + "max_ns": 4838542, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "var_assign_many", + "category": "variables", + "iterations": 10, + "times_ns": [ + 29125, + 27666, + 36000, + 28041, + 34292, + 34750, + 25417, + 25000, + 24375, + 25208 + ], + "mean_ns": 28987.4, + "stddev_ns": 4211.7551733214505, + "min_ns": 24375, + "max_ns": 36000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "var_assign_many", + "category": "variables", + "iterations": 10, + "times_ns": [ + 3970291, + 3961416, + 4429125, + 4179917, + 3943250, + 3754417, + 4292333, + 4816167, + 4835834, + 4230042 + ], + "mean_ns": 4241279.2, + "stddev_ns": 346899.083771001, + "min_ns": 3754417, + "max_ns": 4835834, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "var_default", + "category": "variables", + "iterations": 10, + "times_ns": [ + 20083, + 18791, + 17792, + 17375, + 16708, + 16584, + 16709, + 16542, + 16833, + 16750 + ], + "mean_ns": 17416.7, + "stddev_ns": 1112.9790698840657, + "min_ns": 16542, + "max_ns": 20083, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "var_default", + "category": "variables", + "iterations": 10, + "times_ns": [ + 4295459, + 3723083, + 4179292, + 4099375, + 4093459, + 4119459, + 4043333, + 4138625, + 4461167, + 4142083 + ], + "mean_ns": 4129533.5, + "stddev_ns": 177531.13498496538, + "min_ns": 3723083, + "max_ns": 4461167, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "var_length", + "category": "variables", + "iterations": 10, + "times_ns": [ + 19292, + 18416, + 17625, + 17416, + 17292, + 17042, + 16791, + 16958, + 17167, + 16958 + ], + "mean_ns": 17495.7, + "stddev_ns": 743.4340656709243, + "min_ns": 16791, + "max_ns": 19292, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "var_length", + "category": "variables", + "iterations": 10, + "times_ns": [ + 4426666, + 4351042, + 4537834, + 4288625, + 4196375, + 4445458, + 4041792, + 3866334, + 3869583, + 4041417 + ], + "mean_ns": 4206512.6, + "stddev_ns": 229500.04943363302, + "min_ns": 3866334, + "max_ns": 4537834, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "var_substring", + "category": "variables", + "iterations": 10, + "times_ns": [ + 21167, + 19000, + 18292, + 18041, + 18208, + 17834, + 17959, + 17417, + 17708, + 17583 + ], + "mean_ns": 18320.9, + "stddev_ns": 1037.2371426052964, + "min_ns": 17417, + "max_ns": 21167, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "var_substring", + "category": "variables", + "iterations": 10, + "times_ns": [ + 4176709, + 4015292, + 4064958, + 3787917, + 4229833, + 3899958, + 4520209, + 3940666, + 4075167, + 3909167 + ], + "mean_ns": 4061987.6, + "stddev_ns": 198268.57535383664, + "min_ns": 3787917, + "max_ns": 4520209, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "var_replace", + "category": "variables", + "iterations": 10, + "times_ns": [ + 21416, + 20583, + 19250, + 18667, + 18583, + 18250, + 21250, + 19000, + 18042, + 18167 + ], + "mean_ns": 19320.8, + "stddev_ns": 1220.563378116843, + "min_ns": 18042, + "max_ns": 21416, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "var_replace", + "category": "variables", + "iterations": 10, + "times_ns": [ + 3818375, + 3618125, + 4017584, + 3678000, + 3769000, + 4008375, + 3811250, + 3923041, + 3963542, + 3938625 + ], + "mean_ns": 3854591.7, + "stddev_ns": 130938.94890829847, + "min_ns": 3618125, + "max_ns": 4017584, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "var_nested", + "category": "variables", + "iterations": 10, + "times_ns": [ + 21833, + 19583, + 18750, + 18667, + 18625, + 18333, + 18041, + 18000, + 17833, + 17792 + ], + "mean_ns": 18745.7, + "stddev_ns": 1149.731712183325, + "min_ns": 17792, + "max_ns": 21833, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "var_nested", + "category": "variables", + "iterations": 10, + "times_ns": [ + 3811500, + 3900541, + 3797250, + 3784708, + 4202500, + 3712875, + 3635291, + 3956667, + 3603958, + 4084000 + ], + "mean_ns": 3848929.0, + "stddev_ns": 180803.972576379, + "min_ns": 3603958, + "max_ns": 4202500, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "var_export", + "category": "variables", + "iterations": 10, + "times_ns": [ + 20916, + 19042, + 19042, + 18625, + 18208, + 17917, + 18000, + 17833, + 18208, + 18250 + ], + "mean_ns": 18604.1, + "stddev_ns": 871.7595367989961, + "min_ns": 17833, + "max_ns": 20916, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "var_export", + "category": "variables", + "iterations": 10, + "times_ns": [ + 3709709, + 4075041, + 3764875, + 3608500, + 4001458, + 3624167, + 3582041, + 4032209, + 3647875, + 3987042 + ], + "mean_ns": 3803291.7, + "stddev_ns": 187790.87291508605, + "min_ns": 3582041, + "max_ns": 4075041, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arith_basic", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 19709, + 17792, + 17459, + 16875, + 16500, + 16291, + 16458, + 16500, + 16000, + 16125 + ], + "mean_ns": 16970.9, + "stddev_ns": 1060.46465759119, + "min_ns": 16000, + "max_ns": 19709, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arith_basic", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 4230500, + 4164709, + 3688958, + 3851541, + 3959250, + 3645709, + 3899791, + 3722500, + 3728000, + 3954583 + ], + "mean_ns": 3884554.1, + "stddev_ns": 188691.92341192032, + "min_ns": 3645709, + "max_ns": 4230500, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arith_complex", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 20708, + 18708, + 20042, + 17959, + 17167, + 17333, + 17667, + 17250, + 17125, + 17250 + ], + "mean_ns": 18120.9, + "stddev_ns": 1224.7488273111348, + "min_ns": 17125, + "max_ns": 20708, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arith_complex", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 3417084, + 3929417, + 3592125, + 3659208, + 3934708, + 3512084, + 3554792, + 3848750, + 3556584, + 3683958 + ], + "mean_ns": 3668871.0, + "stddev_ns": 170505.7677405665, + "min_ns": 3417084, + "max_ns": 3934708, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arith_variables", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 22375, + 20500, + 19750, + 19250, + 19417, + 18833, + 18792, + 19084, + 18834, + 18916 + ], + "mean_ns": 19575.1, + "stddev_ns": 1060.4213737943987, + "min_ns": 18792, + "max_ns": 22375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arith_variables", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 3589375, + 3574000, + 3682250, + 3717208, + 3770958, + 3677000, + 3621792, + 3748541, + 3606750, + 3513458 + ], + "mean_ns": 3650133.2, + "stddev_ns": 78350.70448923863, + "min_ns": 3513458, + "max_ns": 3770958, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arith_increment", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 21541, + 17833, + 17416, + 16500, + 16917, + 18208, + 18125, + 18333, + 18000, + 18292 + ], + "mean_ns": 18116.5, + "stddev_ns": 1282.5690819601102, + "min_ns": 16500, + "max_ns": 21541, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arith_increment", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 4385917, + 4540916, + 4501875, + 5096000, + 4525709, + 5130375, + 4484833, + 5131959, + 4987416, + 4635458 + ], + "mean_ns": 4742045.8, + "stddev_ns": 289414.220156785, + "min_ns": 4385917, + "max_ns": 5131959, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arith_modulo", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 19083, + 17500, + 16709, + 16583, + 16250, + 15959, + 15834, + 16167, + 15875, + 15917 + ], + "mean_ns": 16587.7, + "stddev_ns": 963.62264917342, + "min_ns": 15834, + "max_ns": 19083, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arith_modulo", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 4790958, + 5320333, + 4827333, + 5371334, + 4888041, + 5114375, + 4558292, + 4378750, + 4090709, + 4245750 + ], + "mean_ns": 4758587.5, + "stddev_ns": 416364.2435159989, + "min_ns": 4090709, + "max_ns": 5371334, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arith_loop_sum", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 38042, + 37667, + 36500, + 36292, + 36709, + 35791, + 43000, + 36125, + 38792, + 36375 + ], + "mean_ns": 37529.3, + "stddev_ns": 2034.5566617816276, + "min_ns": 35791, + "max_ns": 43000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arith_loop_sum", + "category": "arithmetic", + "iterations": 10, + "times_ns": [ + 4787083, + 4509292, + 4137167, + 20933459, + 5995708, + 7232166, + 9692875, + 4546833, + 4283125, + 4687250 + ], + "mean_ns": 7080495.8, + "stddev_ns": 4903419.7723528175, + "min_ns": 4137167, + "max_ns": 20933459, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_if_simple", + "category": "control", + "iterations": 10, + "times_ns": [ + 22167, + 24917, + 19541, + 19125, + 18875, + 18583, + 18125, + 18459, + 17917, + 16250 + ], + "mean_ns": 19395.9, + "stddev_ns": 2318.726480204166, + "min_ns": 16250, + "max_ns": 24917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_if_simple", + "category": "control", + "iterations": 10, + "times_ns": [ + 4352958, + 3999750, + 4765167, + 4237791, + 4525291, + 4126667, + 4016041, + 4107500, + 4719334, + 4190458 + ], + "mean_ns": 4304095.7, + "stddev_ns": 264455.4674836767, + "min_ns": 3999750, + "max_ns": 4765167, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_if_else", + "category": "control", + "iterations": 10, + "times_ns": [ + 20458, + 21167, + 20042, + 19584, + 18959, + 18959, + 19000, + 22750, + 19458, + 17000 + ], + "mean_ns": 19737.7, + "stddev_ns": 1451.9671518322996, + "min_ns": 17000, + "max_ns": 22750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_if_else", + "category": "control", + "iterations": 10, + "times_ns": [ + 4034709, + 4072292, + 4153958, + 4161166, + 4144708, + 3920333, + 4493292, + 3843875, + 4436166, + 3930959 + ], + "mean_ns": 4119145.8, + "stddev_ns": 201036.77896534256, + "min_ns": 3843875, + "max_ns": 4493292, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_for_list", + "category": "control", + "iterations": 10, + "times_ns": [ + 31917, + 29834, + 29417, + 29000, + 28416, + 28834, + 28125, + 27958, + 27833, + 28500 + ], + "mean_ns": 28983.4, + "stddev_ns": 1149.2375037388922, + "min_ns": 27833, + "max_ns": 31917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_for_list", + "category": "control", + "iterations": 10, + "times_ns": [ + 4490875, + 3936667, + 4171250, + 4036584, + 3684084, + 4437667, + 3837666, + 4429208, + 3992167, + 3796375 + ], + "mean_ns": 4081254.3, + "stddev_ns": 274419.99319730693, + "min_ns": 3684084, + "max_ns": 4490875, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_for_range", + "category": "control", + "iterations": 10, + "times_ns": [ + 35458, + 33500, + 32583, + 31833, + 31542, + 31000, + 30542, + 30750, + 30417, + 30291 + ], + "mean_ns": 31791.6, + "stddev_ns": 1564.155823439596, + "min_ns": 30291, + "max_ns": 35458, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_for_range", + "category": "control", + "iterations": 10, + "times_ns": [ + 3764542, + 3764875, + 4029833, + 3735125, + 4093750, + 3776458, + 3646583, + 3832500, + 3823292, + 3657625 + ], + "mean_ns": 3812458.3, + "stddev_ns": 137920.40768359843, + "min_ns": 3646583, + "max_ns": 4093750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_while", + "category": "control", + "iterations": 10, + "times_ns": [ + 52041, + 49625, + 48917, + 48791, + 48042, + 48042, + 47541, + 47667, + 47583, + 47583 + ], + "mean_ns": 48583.2, + "stddev_ns": 1330.7339929527614, + "min_ns": 47541, + "max_ns": 52041, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_while", + "category": "control", + "iterations": 10, + "times_ns": [ + 3834583, + 3638792, + 3927042, + 3678375, + 3904084, + 3782417, + 3546708, + 4009250, + 3733458, + 3617125 + ], + "mean_ns": 3767183.4, + "stddev_ns": 142934.65824788614, + "min_ns": 3546708, + "max_ns": 4009250, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_case", + "category": "control", + "iterations": 10, + "times_ns": [ + 23458, + 21292, + 20458, + 20458, + 20250, + 20375, + 20291, + 19542, + 19709, + 19583 + ], + "mean_ns": 20541.6, + "stddev_ns": 1089.467319381357, + "min_ns": 19542, + "max_ns": 23458, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_case", + "category": "control", + "iterations": 10, + "times_ns": [ + 3806542, + 3604458, + 4285125, + 3619208, + 3600958, + 3935459, + 3811125, + 3932042, + 3651000, + 3760958 + ], + "mean_ns": 3800687.5, + "stddev_ns": 201384.64426378193, + "min_ns": 3600958, + "max_ns": 4285125, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_function", + "category": "control", + "iterations": 10, + "times_ns": [ + 22750, + 20958, + 20250, + 25500, + 21916, + 19667, + 19084, + 19209, + 20709, + 19292 + ], + "mean_ns": 20933.5, + "stddev_ns": 1906.0212092209258, + "min_ns": 19084, + "max_ns": 25500, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_function", + "category": "control", + "iterations": 10, + "times_ns": [ + 4009333, + 3653375, + 4012792, + 3612667, + 3631458, + 3910417, + 3702500, + 3907375, + 3701292, + 3601333 + ], + "mean_ns": 3774254.2, + "stddev_ns": 158085.618549443, + "min_ns": 3601333, + "max_ns": 4012792, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_function_return", + "category": "control", + "iterations": 10, + "times_ns": [ + 29041, + 25625, + 25666, + 24875, + 24958, + 24792, + 24541, + 24583, + 24458, + 24209 + ], + "mean_ns": 25274.8, + "stddev_ns": 1333.168391464484, + "min_ns": 24209, + "max_ns": 29041, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_function_return", + "category": "control", + "iterations": 10, + "times_ns": [ + 4080500, + 4517167, + 4228000, + 4367041, + 4514000, + 4253458, + 4482958, + 4192917, + 4417959, + 4265500 + ], + "mean_ns": 4331950.0, + "stddev_ns": 142414.66169885738, + "min_ns": 4080500, + "max_ns": 4517167, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "ctrl_nested_loops", + "category": "control", + "iterations": 10, + "times_ns": [ + 40666, + 40167, + 39500, + 39375, + 38458, + 38417, + 38417, + 38750, + 38167, + 41875 + ], + "mean_ns": 39379.2, + "stddev_ns": 1143.927427768038, + "min_ns": 38167, + "max_ns": 41875, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "ctrl_nested_loops", + "category": "control", + "iterations": 10, + "times_ns": [ + 3925334, + 3724292, + 3574833, + 3868791, + 3623708, + 3643166, + 3848583, + 3545958, + 4032542, + 3802625 + ], + "mean_ns": 3758983.2, + "stddev_ns": 153852.2032437625, + "min_ns": 3545958, + "max_ns": 4032542, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "str_concat", + "category": "strings", + "iterations": 10, + "times_ns": [ + 20333, + 18791, + 18583, + 18084, + 17750, + 17875, + 17667, + 17541, + 18000, + 17958 + ], + "mean_ns": 18258.2, + "stddev_ns": 784.2385861458233, + "min_ns": 17541, + "max_ns": 20333, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "str_concat", + "category": "strings", + "iterations": 10, + "times_ns": [ + 3741459, + 3805459, + 3502459, + 3976750, + 3865083, + 3662083, + 3839833, + 3622541, + 3980750, + 3729000 + ], + "mean_ns": 3772541.7, + "stddev_ns": 144822.4081425592, + "min_ns": 3502459, + "max_ns": 3980750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "str_printf", + "category": "strings", + "iterations": 10, + "times_ns": [ + 18792, + 17959, + 17000, + 16625, + 17208, + 16666, + 16417, + 28667, + 16750, + 19709 + ], + "mean_ns": 18579.3, + "stddev_ns": 3512.0430535515934, + "min_ns": 16417, + "max_ns": 28667, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "str_printf", + "category": "strings", + "iterations": 10, + "times_ns": [ + 3679667, + 3800583, + 3539541, + 3856208, + 3797542, + 3628958, + 3790667, + 3684375, + 3653125, + 4094291 + ], + "mean_ns": 3752495.7, + "stddev_ns": 146222.13474235014, + "min_ns": 3539541, + "max_ns": 4094291, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "str_printf_pad", + "category": "strings", + "iterations": 10, + "times_ns": [ + 18542, + 17083, + 16875, + 16417, + 16250, + 16208, + 16125, + 16417, + 16167, + 16208 + ], + "mean_ns": 16629.2, + "stddev_ns": 705.8095777190899, + "min_ns": 16125, + "max_ns": 18542, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "str_printf_pad", + "category": "strings", + "iterations": 10, + "times_ns": [ + 3548209, + 3856250, + 3593500, + 3624125, + 3861084, + 3575250, + 3959208, + 3636708, + 3833625, + 4662958 + ], + "mean_ns": 3815091.7, + "stddev_ns": 314633.4175179267, + "min_ns": 3548209, + "max_ns": 4662958, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "str_echo_escape", + "category": "strings", + "iterations": 10, + "times_ns": [ + 24000, + 17625, + 15291, + 19583, + 20583, + 16542, + 16208, + 16333, + 70292, + 18042 + ], + "mean_ns": 23449.9, + "stddev_ns": 15805.739238960005, + "min_ns": 15291, + "max_ns": 70292, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "str_echo_escape", + "category": "strings", + "iterations": 10, + "times_ns": [ + 4712542, + 4809750, + 4513333, + 4496625, + 4862958, + 4866083, + 5616250, + 4761708, + 5211208, + 4674084 + ], + "mean_ns": 4852454.1, + "stddev_ns": 318661.5553289885, + "min_ns": 4496625, + "max_ns": 5616250, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "str_prefix_strip", + "category": "strings", + "iterations": 10, + "times_ns": [ + 21959, + 19542, + 18709, + 18417, + 18167, + 18333, + 17833, + 18125, + 17833, + 17750 + ], + "mean_ns": 18666.8, + "stddev_ns": 1206.419230615958, + "min_ns": 17750, + "max_ns": 21959, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "str_prefix_strip", + "category": "strings", + "iterations": 10, + "times_ns": [ + 5362375, + 5358708, + 4932125, + 5639542, + 5080750, + 5055416, + 5456333, + 5122041, + 5107833, + 5914375 + ], + "mean_ns": 5302949.8, + "stddev_ns": 289181.11050302023, + "min_ns": 4932125, + "max_ns": 5914375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "str_suffix_strip", + "category": "strings", + "iterations": 10, + "times_ns": [ + 23875, + 19958, + 19458, + 18416, + 18208, + 18792, + 18167, + 29583, + 50000, + 19958 + ], + "mean_ns": 23641.5, + "stddev_ns": 9407.874661686348, + "min_ns": 18167, + "max_ns": 50000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "str_suffix_strip", + "category": "strings", + "iterations": 10, + "times_ns": [ + 5132708, + 5345084, + 4735209, + 5397125, + 5053292, + 4955833, + 5607833, + 4985250, + 5679709, + 4606875 + ], + "mean_ns": 5149891.8, + "stddev_ns": 336282.95710630354, + "min_ns": 4606875, + "max_ns": 5679709, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "str_uppercase", + "category": "strings", + "iterations": 10, + "times_ns": [ + 25958, + 17125, + 17375, + 17583, + 17333, + 17292, + 16750, + 17334, + 17209, + 17291 + ], + "mean_ns": 18125.0, + "stddev_ns": 2618.8589881855037, + "min_ns": 16750, + "max_ns": 25958, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "str_uppercase", + "category": "strings", + "iterations": 10, + "times_ns": [ + 4864459, + 4499791, + 4708417, + 4317500, + 4772875, + 4078916, + 4338500, + 4538917, + 4765833, + 4899625 + ], + "mean_ns": 4578483.3, + "stddev_ns": 256459.4756576758, + "min_ns": 4078916, + "max_ns": 4899625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "str_lowercase", + "category": "strings", + "iterations": 10, + "times_ns": [ + 20042, + 18500, + 17667, + 17541, + 17125, + 16958, + 17167, + 16792, + 17166, + 17041 + ], + "mean_ns": 17599.9, + "stddev_ns": 935.9654320539834, + "min_ns": 16792, + "max_ns": 20042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "str_lowercase", + "category": "strings", + "iterations": 10, + "times_ns": [ + 4339875, + 4283292, + 3995333, + 4467292, + 4105875, + 4688916, + 3837708, + 4057375, + 4176667, + 3825000 + ], + "mean_ns": 4177733.3, + "stddev_ns": 259912.1565475728, + "min_ns": 3825000, + "max_ns": 4688916, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arr_create", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 20916, + 19750, + 18416, + 18167, + 17625, + 16292, + 15459, + 15500, + 17167, + 17208 + ], + "mean_ns": 17650.0, + "stddev_ns": 1662.1439167532997, + "min_ns": 15459, + "max_ns": 20916, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arr_create", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 3963292, + 3704625, + 4509834, + 3853584, + 4473375, + 3893708, + 4015584, + 4230542, + 3818958, + 4338417 + ], + "mean_ns": 4080191.9, + "stddev_ns": 272347.4689816118, + "min_ns": 3704625, + "max_ns": 4509834, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arr_all", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 21625, + 19542, + 19000, + 18083, + 17958, + 18042, + 17708, + 17500, + 17708, + 17458 + ], + "mean_ns": 18462.4, + "stddev_ns": 1229.022391984784, + "min_ns": 17458, + "max_ns": 21625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arr_all", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 3796334, + 4379375, + 3707750, + 4078666, + 3865666, + 3645833, + 3996833, + 3677792, + 3733417, + 4005708 + ], + "mean_ns": 3888737.4, + "stddev_ns": 217167.43779867186, + "min_ns": 3645833, + "max_ns": 4379375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arr_length", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 20208, + 18917, + 18417, + 17959, + 18500, + 17917, + 17625, + 18083, + 17750, + 17916 + ], + "mean_ns": 18329.2, + "stddev_ns": 726.7706378218645, + "min_ns": 17625, + "max_ns": 20208, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arr_length", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 3635000, + 3957083, + 3673000, + 3855958, + 3772709, + 3573459, + 3930708, + 3658541, + 3742375, + 3749541 + ], + "mean_ns": 3754837.4, + "stddev_ns": 120744.09587155805, + "min_ns": 3573459, + "max_ns": 3957083, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arr_iterate", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 26166, + 24000, + 23209, + 23041, + 22625, + 22583, + 22250, + 22292, + 22333, + 22083 + ], + "mean_ns": 23058.2, + "stddev_ns": 1170.4316126967865, + "min_ns": 22083, + "max_ns": 26166, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arr_iterate", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 4856166, + 4448792, + 3789666, + 4317334, + 3964542, + 3697542, + 4144542, + 3651791, + 3978416, + 3631750 + ], + "mean_ns": 4048054.1, + "stddev_ns": 377787.99784970673, + "min_ns": 3631750, + "max_ns": 4856166, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arr_slice", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 22125, + 20458, + 19958, + 19958, + 19167, + 19083, + 19083, + 18833, + 18791, + 18834 + ], + "mean_ns": 19629.0, + "stddev_ns": 995.5551215276831, + "min_ns": 18791, + "max_ns": 22125, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arr_slice", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 4783916, + 3930417, + 4798167, + 4399958, + 5140541, + 4335750, + 3779458, + 4737833, + 3814250, + 4846167 + ], + "mean_ns": 4456645.7, + "stddev_ns": 456973.7297193461, + "min_ns": 3779458, + "max_ns": 5140541, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "arr_assign_index", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 22500, + 20708, + 19875, + 19792, + 19208, + 18958, + 19459, + 19041, + 19084, + 19250 + ], + "mean_ns": 19787.5, + "stddev_ns": 1033.708687203508, + "min_ns": 18958, + "max_ns": 22500, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "arr_assign_index", + "category": "arrays", + "iterations": 10, + "times_ns": [ + 4193584, + 4021500, + 3626625, + 4269625, + 3844792, + 4571959, + 4205208, + 3769792, + 4683583, + 3909334 + ], + "mean_ns": 4109600.2, + "stddev_ns": 324308.5285378107, + "min_ns": 3626625, + "max_ns": 4683583, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "pipe_simple", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 27250, + 27583, + 25291, + 24792, + 22583, + 22083, + 21875, + 23375, + 21583, + 22167 + ], + "mean_ns": 23858.2, + "stddev_ns": 2128.0053477376414, + "min_ns": 21583, + "max_ns": 27583, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "pipe_simple", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 6605750, + 7013625, + 6195000, + 6603875, + 5771500, + 6293333, + 6565791, + 5733500, + 6446459, + 6127083 + ], + "mean_ns": 6335591.6, + "stddev_ns": 376259.9408632814, + "min_ns": 5733500, + "max_ns": 7013625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "pipe_multi", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 34666, + 32834, + 32459, + 31417, + 31500, + 31250, + 31292, + 31750, + 31166, + 31750 + ], + "mean_ns": 32008.4, + "stddev_ns": 1024.0750167834387, + "min_ns": 31166, + "max_ns": 34666, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "pipe_multi", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 6797083, + 6562334, + 5986125, + 6526875, + 6923292, + 6307459, + 6855333, + 6628125, + 6330167, + 7091625 + ], + "mean_ns": 6600841.8, + "stddev_ns": 315124.1073601954, + "min_ns": 5986125, + "max_ns": 7091625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "pipe_command_subst", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 22333, + 21291, + 20458, + 20250, + 17708, + 17792, + 19500, + 19875, + 19916, + 19958 + ], + "mean_ns": 19908.1, + "stddev_ns": 1330.4266571292085, + "min_ns": 17708, + "max_ns": 22333, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "pipe_command_subst", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 4601625, + 4259416, + 4720000, + 4474167, + 4726584, + 4310833, + 4651708, + 4573875, + 4978417, + 4535167 + ], + "mean_ns": 4583179.2, + "stddev_ns": 198445.1289539756, + "min_ns": 4259416, + "max_ns": 4978417, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "pipe_heredoc", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 23750, + 21708, + 20667, + 20542, + 20583, + 20291, + 19625, + 19708, + 20125, + 19792 + ], + "mean_ns": 20679.1, + "stddev_ns": 1175.4146885248629, + "min_ns": 19625, + "max_ns": 23750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "pipe_heredoc", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 5343583, + 5665084, + 5251458, + 5437041, + 5214208, + 5634584, + 5605209, + 5199750, + 5766000, + 5236042 + ], + "mean_ns": 5435295.9, + "stddev_ns": 204212.7574582205, + "min_ns": 5199750, + "max_ns": 5766000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "pipe_herestring", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 23709, + 21417, + 20542, + 20250, + 19584, + 19750, + 19875, + 19584, + 19208, + 19291 + ], + "mean_ns": 20321.0, + "stddev_ns": 1290.1397598709993, + "min_ns": 19208, + "max_ns": 23709, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "pipe_herestring", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 7791000, + 6859208, + 7557292, + 7210250, + 7775500, + 7158167, + 7134875, + 7007959, + 7076334, + 6958250 + ], + "mean_ns": 7252883.5, + "stddev_ns": 318280.3450664995, + "min_ns": 6859208, + "max_ns": 7791000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "pipe_discard", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 22083, + 20875, + 20666, + 19750, + 19541, + 19667, + 19709, + 19292, + 20333, + 20000 + ], + "mean_ns": 20191.6, + "stddev_ns": 791.3196825556661, + "min_ns": 19292, + "max_ns": 22083, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "pipe_discard", + "category": "pipes", + "iterations": 10, + "times_ns": [ + 4894042, + 5239459, + 4905625, + 5390209, + 5539667, + 4846833, + 5294791, + 6124958, + 6112166, + 6257083 + ], + "mean_ns": 5460483.3, + "stddev_ns": 509273.2503902105, + "min_ns": 4846833, + "max_ns": 6257083, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_grep_simple", + "category": "tools", + "iterations": 10, + "times_ns": [ + 24541, + 23000, + 22333, + 21708, + 23416, + 22583, + 20792, + 20250, + 20292, + 20125 + ], + "mean_ns": 21904.0, + "stddev_ns": 1444.3085542916374, + "min_ns": 20125, + "max_ns": 24541, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_grep_simple", + "category": "tools", + "iterations": 10, + "times_ns": [ + 8100958, + 7676000, + 9419000, + 7670000, + 8407375, + 7646083, + 6909916, + 7852416, + 7695125, + 8081416 + ], + "mean_ns": 7945828.9, + "stddev_ns": 617182.109698499, + "min_ns": 6909916, + "max_ns": 9419000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_grep_case", + "category": "tools", + "iterations": 10, + "times_ns": [ + 83667, + 70167, + 67500, + 67250, + 65041, + 67250, + 67583, + 66541, + 97083, + 152208 + ], + "mean_ns": 80429.0, + "stddev_ns": 25796.495444148997, + "min_ns": 65041, + "max_ns": 152208, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_grep_case", + "category": "tools", + "iterations": 10, + "times_ns": [ + 7357000, + 7175833, + 7470500, + 7373417, + 8076458, + 7379042, + 8332375, + 8334541, + 7712584, + 6975542 + ], + "mean_ns": 7618729.2, + "stddev_ns": 453646.35522679996, + "min_ns": 6975542, + "max_ns": 8334541, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_grep_count", + "category": "tools", + "iterations": 10, + "times_ns": [ + 29375, + 24125, + 22959, + 22541, + 22167, + 21125, + 20875, + 20875, + 20667, + 20541 + ], + "mean_ns": 22525.0, + "stddev_ns": 2541.50931534787, + "min_ns": 20541, + "max_ns": 29375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_grep_count", + "category": "tools", + "iterations": 10, + "times_ns": [ + 7402875, + 7611375, + 7232542, + 6630125, + 7399000, + 7111750, + 6895792, + 7541208, + 7201250, + 7209417 + ], + "mean_ns": 7223533.4, + "stddev_ns": 280554.9243731786, + "min_ns": 6630125, + "max_ns": 7611375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_grep_invert", + "category": "tools", + "iterations": 10, + "times_ns": [ + 24834, + 22833, + 22000, + 21625, + 21208, + 21042, + 21292, + 20500, + 20667, + 20750 + ], + "mean_ns": 21675.1, + "stddev_ns": 1242.8254463117498, + "min_ns": 20500, + "max_ns": 24834, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_grep_invert", + "category": "tools", + "iterations": 10, + "times_ns": [ + 6489208, + 7369417, + 6828000, + 7010750, + 7090625, + 6723000, + 6780666, + 6704542, + 6784459, + 6613625 + ], + "mean_ns": 6839429.2, + "stddev_ns": 241937.06181104208, + "min_ns": 6489208, + "max_ns": 7369417, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_grep_regex", + "category": "tools", + "iterations": 10, + "times_ns": [ + 36583, + 35750, + 32334, + 30667, + 30083, + 31000, + 30791, + 30333, + 30042, + 29833 + ], + "mean_ns": 31741.6, + "stddev_ns": 2318.021578846927, + "min_ns": 29833, + "max_ns": 36583, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_grep_regex", + "category": "tools", + "iterations": 10, + "times_ns": [ + 7278833, + 6993500, + 7023792, + 6850417, + 7068750, + 7556959, + 6486750, + 6609250, + 6759125, + 6145333 + ], + "mean_ns": 6877270.9, + "stddev_ns": 383356.80075732316, + "min_ns": 6145333, + "max_ns": 7556959, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_sed_replace", + "category": "tools", + "iterations": 10, + "times_ns": [ + 62416, + 59250, + 56042, + 55250, + 59250, + 54250, + 53333, + 53958, + 55417, + 54208 + ], + "mean_ns": 56337.4, + "stddev_ns": 2821.564076890688, + "min_ns": 53333, + "max_ns": 62416, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_sed_replace", + "category": "tools", + "iterations": 10, + "times_ns": [ + 6365583, + 7022583, + 6729083, + 6655125, + 6613708, + 5790583, + 6527042, + 6465875, + 6037708, + 6474208 + ], + "mean_ns": 6468149.8, + "stddev_ns": 330156.9703885714, + "min_ns": 5790583, + "max_ns": 7022583, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_sed_global", + "category": "tools", + "iterations": 10, + "times_ns": [ + 212792, + 63250, + 57292, + 54291, + 53208, + 52500, + 52458, + 52208, + 52292, + 52542 + ], + "mean_ns": 70283.3, + "stddev_ns": 47616.62991025299, + "min_ns": 52208, + "max_ns": 212792, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_sed_global", + "category": "tools", + "iterations": 10, + "times_ns": [ + 6758917, + 6599959, + 7174000, + 7292500, + 6969167, + 6851500, + 6808000, + 6666041, + 6551958, + 7824417 + ], + "mean_ns": 6949645.9, + "stddev_ns": 368840.2461669415, + "min_ns": 6551958, + "max_ns": 7824417, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_sed_delete", + "category": "tools", + "iterations": 10, + "times_ns": [ + 24000, + 22208, + 21291, + 20500, + 20750, + 20000, + 19500, + 19625, + 19417, + 19125 + ], + "mean_ns": 20641.6, + "stddev_ns": 1439.9332762319232, + "min_ns": 19125, + "max_ns": 24000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_sed_delete", + "category": "tools", + "iterations": 10, + "times_ns": [ + 6748458, + 6080875, + 6606750, + 6806416, + 6687792, + 8134250, + 6850333, + 6804459, + 7036250, + 6732500 + ], + "mean_ns": 6848808.3, + "stddev_ns": 488784.0907077582, + "min_ns": 6080875, + "max_ns": 8134250, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_sed_lines", + "category": "tools", + "iterations": 10, + "times_ns": [ + 22042, + 21208, + 19333, + 17416, + 19084, + 19250, + 19000, + 18958, + 19209, + 18833 + ], + "mean_ns": 19433.3, + "stddev_ns": 1225.331551050572, + "min_ns": 17416, + "max_ns": 22042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_sed_lines", + "category": "tools", + "iterations": 10, + "times_ns": [ + 6501875, + 6467459, + 6308208, + 6419500, + 6579667, + 7300833, + 7121208, + 6908417, + 6855042, + 6710958 + ], + "mean_ns": 6717316.7, + "stddev_ns": 307083.6404043856, + "min_ns": 6308208, + "max_ns": 7300833, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_sed_backrefs", + "category": "tools", + "iterations": 10, + "times_ns": [ + 83041, + 81709, + 75000, + 73208, + 72458, + 72542, + 72125, + 71375, + 71209, + 80041 + ], + "mean_ns": 75270.8, + "stddev_ns": 4310.9098761166415, + "min_ns": 71209, + "max_ns": 83041, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_sed_backrefs", + "category": "tools", + "iterations": 10, + "times_ns": [ + 6745541, + 7231167, + 7291333, + 6357916, + 6971959, + 6895125, + 6471708, + 7132583, + 6969417, + 6533083 + ], + "mean_ns": 6859983.2, + "stddev_ns": 307683.2557890663, + "min_ns": 6357916, + "max_ns": 7291333, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_awk_print", + "category": "tools", + "iterations": 10, + "times_ns": [ + 23291, + 18042, + 17625, + 17584, + 19416, + 23292, + 24292, + 20958, + 18708, + 19250 + ], + "mean_ns": 20245.8, + "stddev_ns": 2414.2713517746924, + "min_ns": 17584, + "max_ns": 24292, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_awk_print", + "category": "tools", + "iterations": 10, + "times_ns": [ + 6473792, + 6453042, + 6703500, + 6444916, + 6628000, + 7689000, + 6441458, + 6703625, + 6401500, + 6075666 + ], + "mean_ns": 6601449.9, + "stddev_ns": 401145.1717182821, + "min_ns": 6075666, + "max_ns": 7689000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_awk_sum", + "category": "tools", + "iterations": 10, + "times_ns": [ + 25333, + 23500, + 22542, + 22500, + 21792, + 21917, + 21959, + 21875, + 21959, + 21834 + ], + "mean_ns": 22521.1, + "stddev_ns": 1062.2568851271335, + "min_ns": 21792, + "max_ns": 25333, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_awk_sum", + "category": "tools", + "iterations": 10, + "times_ns": [ + 6003084, + 5718250, + 5839375, + 6025333, + 5688083, + 6350250, + 5574000, + 5942417, + 5550000, + 5631834 + ], + "mean_ns": 5832262.6, + "stddev_ns": 238176.75250040673, + "min_ns": 5550000, + "max_ns": 6350250, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_awk_pattern", + "category": "tools", + "iterations": 10, + "times_ns": [ + 36750, + 32583, + 31542, + 30334, + 30166, + 30042, + 29583, + 29458, + 29166, + 29083 + ], + "mean_ns": 30870.7, + "stddev_ns": 2217.564928023529, + "min_ns": 29083, + "max_ns": 36750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_awk_pattern", + "category": "tools", + "iterations": 10, + "times_ns": [ + 5584250, + 5873916, + 6052250, + 6381417, + 5798167, + 6872417, + 6655458, + 7853292, + 7497291, + 7348584 + ], + "mean_ns": 6591704.2, + "stddev_ns": 745824.3209705352, + "min_ns": 5584250, + "max_ns": 7853292, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_awk_fieldsep", + "category": "tools", + "iterations": 10, + "times_ns": [ + 23375, + 21792, + 21000, + 20458, + 20375, + 20041, + 19917, + 20208, + 20083, + 19667 + ], + "mean_ns": 20691.6, + "stddev_ns": 1064.8372833442675, + "min_ns": 19667, + "max_ns": 23375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_awk_fieldsep", + "category": "tools", + "iterations": 10, + "times_ns": [ + 7614708, + 7546583, + 8203291, + 7874333, + 7721625, + 7377917, + 6966708, + 6839416, + 7251083, + 7154875 + ], + "mean_ns": 7455053.9, + "stddev_ns": 399466.42212067085, + "min_ns": 6839416, + "max_ns": 8203291, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_awk_nf", + "category": "tools", + "iterations": 10, + "times_ns": [ + 42917, + 22125, + 27041, + 20625, + 19875, + 19541, + 19875, + 19208, + 28333, + 19167 + ], + "mean_ns": 23870.7, + "stddev_ns": 7077.072898451733, + "min_ns": 19167, + "max_ns": 42917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_awk_nf", + "category": "tools", + "iterations": 10, + "times_ns": [ + 7433292, + 7168708, + 7490291, + 7270125, + 7400125, + 7949042, + 7805792, + 7847375, + 7218291, + 7307000 + ], + "mean_ns": 7489004.1, + "stddev_ns": 266145.23490472266, + "min_ns": 7168708, + "max_ns": 7949042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_awk_compute", + "category": "tools", + "iterations": 10, + "times_ns": [ + 23667, + 21791, + 21458, + 20667, + 20333, + 19917, + 20208, + 19750, + 19625, + 19750 + ], + "mean_ns": 20716.6, + "stddev_ns": 1204.8541156505214, + "min_ns": 19625, + "max_ns": 23667, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_awk_compute", + "category": "tools", + "iterations": 10, + "times_ns": [ + 7166875, + 7656541, + 7357417, + 7364084, + 7240083, + 7187375, + 7259500, + 7119167, + 6941584, + 6570083 + ], + "mean_ns": 7186270.9, + "stddev_ns": 271064.61530987406, + "min_ns": 6570083, + "max_ns": 7656541, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_jq_identity", + "category": "tools", + "iterations": 10, + "times_ns": [ + 277000, + 258291, + 254250, + 237875, + 234584, + 228500, + 532709, + 344375, + 274667, + 249125 + ], + "mean_ns": 289137.6, + "stddev_ns": 87054.8392361964, + "min_ns": 228500, + "max_ns": 532709, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_jq_identity", + "category": "tools", + "iterations": 10, + "times_ns": [ + 7498541, + 6901459, + 6541000, + 7854500, + 7521333, + 8092917, + 7854083, + 7805666, + 8073500, + 7682916 + ], + "mean_ns": 7582591.5, + "stddev_ns": 476294.4259508503, + "min_ns": 6541000, + "max_ns": 8092917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_jq_field", + "category": "tools", + "iterations": 10, + "times_ns": [ + 251500, + 237375, + 230750, + 226125, + 224500, + 223750, + 223208, + 233500, + 444334, + 304375 + ], + "mean_ns": 259941.7, + "stddev_ns": 65700.50434060609, + "min_ns": 223208, + "max_ns": 444334, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_jq_field", + "category": "tools", + "iterations": 10, + "times_ns": [ + 8507959, + 7760167, + 7664459, + 8132792, + 7898500, + 7454708, + 8461375, + 7831375, + 8090625, + 8208458 + ], + "mean_ns": 8001041.8, + "stddev_ns": 323476.02120491094, + "min_ns": 7454708, + "max_ns": 8507959, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_jq_array", + "category": "tools", + "iterations": 10, + "times_ns": [ + 267041, + 249500, + 242584, + 241542, + 332500, + 337750, + 671042, + 350584, + 287834, + 259792 + ], + "mean_ns": 324016.9, + "stddev_ns": 122089.83568868459, + "min_ns": 241542, + "max_ns": 671042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_jq_array", + "category": "tools", + "iterations": 10, + "times_ns": [ + 8300167, + 8379625, + 8315750, + 8209083, + 8028459, + 8182541, + 7804000, + 8019750, + 7732792, + 7742792 + ], + "mean_ns": 8071495.9, + "stddev_ns": 231733.84714471473, + "min_ns": 7732792, + "max_ns": 8379625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_jq_filter", + "category": "tools", + "iterations": 10, + "times_ns": [ + 258625, + 236125, + 233833, + 223750, + 225417, + 224666, + 224458, + 224917, + 224250, + 223250 + ], + "mean_ns": 229929.1, + "stddev_ns": 10465.378487661113, + "min_ns": 223250, + "max_ns": 258625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_jq_filter", + "category": "tools", + "iterations": 10, + "times_ns": [ + 8527500, + 8199333, + 7364916, + 7849583, + 7915584, + 7666208, + 8207875, + 7503708, + 7778917, + 8632375 + ], + "mean_ns": 7964599.9, + "stddev_ns": 398528.78738641954, + "min_ns": 7364916, + "max_ns": 8632375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "tool_jq_map", + "category": "tools", + "iterations": 10, + "times_ns": [ + 264125, + 325083, + 253166, + 372750, + 323125, + 281458, + 264000, + 277417, + 255750, + 249708 + ], + "mean_ns": 286658.2, + "stddev_ns": 38463.012544001285, + "min_ns": 249708, + "max_ns": 372750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "tool_jq_map", + "category": "tools", + "iterations": 10, + "times_ns": [ + 8113875, + 7961500, + 7645500, + 7659667, + 7722875, + 7499583, + 8037042, + 8114375, + 7901666, + 7759959 + ], + "mean_ns": 7841604.2, + "stddev_ns": 203473.0250125554, + "min_ns": 7499583, + "max_ns": 8114375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "complex_fibonacci", + "category": "complex", + "iterations": 10, + "times_ns": [ + 2105375, + 2100833, + 2277542, + 2146459, + 2133584, + 2136875, + 2114042, + 2391416, + 2165458, + 2184791 + ], + "mean_ns": 2175637.5, + "stddev_ns": 86937.47629331093, + "min_ns": 2100833, + "max_ns": 2391416, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "complex_fibonacci", + "category": "complex", + "iterations": 10, + "times_ns": [ + 76456916, + 75682750, + 75091667, + 74241625, + 72794625, + 72262375, + 75950000, + 86311666, + 83883500, + 88337167 + ], + "mean_ns": 78101229.1, + "stddev_ns": 5522745.8843305195, + "min_ns": 72262375, + "max_ns": 88337167, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "complex_fibonacci_iter", + "category": "complex", + "iterations": 10, + "times_ns": [ + 51042, + 53458, + 73209, + 170917, + 68500, + 52208, + 56833, + 43375, + 42542, + 42500 + ], + "mean_ns": 65458.4, + "stddev_ns": 36513.803327508904, + "min_ns": 42500, + "max_ns": 170917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "complex_fibonacci_iter", + "category": "complex", + "iterations": 10, + "times_ns": [ + 4322542, + 4472334, + 4665459, + 4379000, + 4616667, + 4241917, + 4715209, + 4546375, + 4700708, + 4365417 + ], + "mean_ns": 4502562.8, + "stddev_ns": 161644.24326266616, + "min_ns": 4241917, + "max_ns": 4715209, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "complex_nested_subst", + "category": "complex", + "iterations": 10, + "times_ns": [ + 26334, + 25000, + 24375, + 23792, + 23791, + 24125, + 31875, + 24416, + 23625, + 23500 + ], + "mean_ns": 25083.3, + "stddev_ns": 2398.6385325846827, + "min_ns": 23500, + "max_ns": 31875, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "complex_nested_subst", + "category": "complex", + "iterations": 10, + "times_ns": [ + 5186000, + 5807500, + 5110083, + 5480875, + 5057792, + 5251333, + 4990042, + 5630083, + 4975375, + 5267500 + ], + "mean_ns": 5275658.3, + "stddev_ns": 265689.83427223936, + "min_ns": 4975375, + "max_ns": 5807500, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "complex_loop_compute", + "category": "complex", + "iterations": 10, + "times_ns": [ + 53166, + 50041, + 49500, + 48791, + 48125, + 48542, + 48292, + 48375, + 48375, + 48000 + ], + "mean_ns": 49120.7, + "stddev_ns": 1478.010016880806, + "min_ns": 48000, + "max_ns": 53166, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "complex_loop_compute", + "category": "complex", + "iterations": 10, + "times_ns": [ + 3930000, + 3896500, + 3708084, + 3933083, + 3727041, + 3962625, + 3813000, + 3703500, + 3910792, + 3875500 + ], + "mean_ns": 3846012.5, + "stddev_ns": 95092.9324148225, + "min_ns": 3703500, + "max_ns": 3962625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "complex_string_build", + "category": "complex", + "iterations": 10, + "times_ns": [ + 33959, + 46834, + 31375, + 27333, + 26000, + 25292, + 66291, + 26583, + 24292, + 23583 + ], + "mean_ns": 33154.2, + "stddev_ns": 12842.149203307054, + "min_ns": 23583, + "max_ns": 66291, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "complex_string_build", + "category": "complex", + "iterations": 10, + "times_ns": [ + 4095375, + 3774291, + 3727625, + 4084916, + 3561125, + 3907250, + 3616208, + 3731125, + 3848333, + 3653125 + ], + "mean_ns": 3799937.3, + "stddev_ns": 174733.5140155145, + "min_ns": 3561125, + "max_ns": 4095375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "complex_json_transform", + "category": "complex", + "iterations": 10, + "times_ns": [ + 272125, + 258791, + 238458, + 232250, + 232333, + 236917, + 227458, + 225958, + 226625, + 225791 + ], + "mean_ns": 237670.6, + "stddev_ns": 14821.774989521331, + "min_ns": 225791, + "max_ns": 272125, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "complex_json_transform", + "category": "complex", + "iterations": 10, + "times_ns": [ + 6764208, + 6669792, + 6310000, + 6453792, + 6687750, + 6387833, + 6672000, + 6341208, + 6324833, + 7272334 + ], + "mean_ns": 6588375.0, + "stddev_ns": 280817.1374115191, + "min_ns": 6310000, + "max_ns": 7272334, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "complex_pipeline_text", + "category": "complex", + "iterations": 10, + "times_ns": [ + 73791, + 68667, + 66459, + 65542, + 65375, + 64500, + 64250, + 64250, + 63750, + 63917 + ], + "mean_ns": 66050.1, + "stddev_ns": 2938.961192326295, + "min_ns": 63750, + "max_ns": 73791, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "complex_pipeline_text", + "category": "complex", + "iterations": 10, + "times_ns": [ + 6305834, + 5903791, + 6306167, + 5982750, + 6519292, + 6291667, + 5724125, + 6037042, + 5796000, + 6362708 + ], + "mean_ns": 6122937.6, + "stddev_ns": 255161.8908642903, + "min_ns": 5724125, + "max_ns": 6519292, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_loop_1000", + "category": "large", + "iterations": 10, + "times_ns": [ + 2403541, + 2425208, + 2330500, + 2320917, + 2370208, + 2406375, + 2420333, + 2469208, + 2421250, + 2494000 + ], + "mean_ns": 2406154.0, + "stddev_ns": 51742.22667802382, + "min_ns": 2320917, + "max_ns": 2494000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_loop_1000", + "category": "large", + "iterations": 10, + "times_ns": [ + 5428542, + 5459875, + 5332708, + 5511208, + 5210584, + 5760750, + 5473292, + 5306959, + 5430958, + 5269042 + ], + "mean_ns": 5418391.8, + "stddev_ns": 146992.5967093581, + "min_ns": 5210584, + "max_ns": 5760750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_string_append_100", + "category": "large", + "iterations": 10, + "times_ns": [ + 178792, + 178209, + 177167, + 175166, + 175292, + 176125, + 174500, + 185208, + 179667, + 175542 + ], + "mean_ns": 177566.8, + "stddev_ns": 3023.1535455547078, + "min_ns": 174500, + "max_ns": 185208, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_string_append_100", + "category": "large", + "iterations": 10, + "times_ns": [ + 3971375, + 3953291, + 4350791, + 4258292, + 4493042, + 3918584, + 4180209, + 3993875, + 4144750, + 4096333 + ], + "mean_ns": 4136054.2, + "stddev_ns": 178920.97093119074, + "min_ns": 3918584, + "max_ns": 4493042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_array_fill_200", + "category": "large", + "iterations": 10, + "times_ns": [ + 734625, + 768042, + 773166, + 748792, + 734917, + 727333, + 727125, + 739125, + 730834, + 725292 + ], + "mean_ns": 740925.1, + "stddev_ns": 16239.245539433166, + "min_ns": 725292, + "max_ns": 773166, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_array_fill_200", + "category": "large", + "iterations": 10, + "times_ns": [ + 4096292, + 4243417, + 3924208, + 4418417, + 4131000, + 4043917, + 4216084, + 3817542, + 4276709, + 3996958 + ], + "mean_ns": 4116454.4, + "stddev_ns": 170110.24944617535, + "min_ns": 3817542, + "max_ns": 4418417, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_nested_loops", + "category": "large", + "iterations": 10, + "times_ns": [ + 1082667, + 1075000, + 1072708, + 1068750, + 1080791, + 1078042, + 1127375, + 1101167, + 1073166, + 1070542 + ], + "mean_ns": 1083020.8, + "stddev_ns": 17186.17352874106, + "min_ns": 1068750, + "max_ns": 1127375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_nested_loops", + "category": "large", + "iterations": 10, + "times_ns": [ + 4626417, + 4759625, + 4364750, + 4826042, + 4420667, + 4611375, + 4585667, + 4389500, + 4730666, + 4611459 + ], + "mean_ns": 4592616.8, + "stddev_ns": 150359.81610643186, + "min_ns": 4364750, + "max_ns": 4826042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_fibonacci_12", + "category": "large", + "iterations": 10, + "times_ns": [ + 5937334, + 6279417, + 6168125, + 6509917, + 5914084, + 5964583, + 6267000, + 6044958, + 6103167, + 6196750 + ], + "mean_ns": 6138533.5, + "stddev_ns": 175973.6005298806, + "min_ns": 5914084, + "max_ns": 6509917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_fibonacci_12", + "category": "large", + "iterations": 10, + "times_ns": [ + 193301209, + 184140291, + 184432208, + 186394583, + 182800416, + 184807666, + 188809792, + 182098958, + 181616042, + 186620041 + ], + "mean_ns": 185502120.6, + "stddev_ns": 3335814.6749056135, + "min_ns": 181616042, + "max_ns": 193301209, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_function_calls_500", + "category": "large", + "iterations": 10, + "times_ns": [ + 3026250, + 2951791, + 2970208, + 3077541, + 2912625, + 2910167, + 2959666, + 2966917, + 2928959, + 2957125 + ], + "mean_ns": 2966124.9, + "stddev_ns": 48732.82301580732, + "min_ns": 2910167, + "max_ns": 3077541, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_function_calls_500", + "category": "large", + "iterations": 10, + "times_ns": [ + 244477208, + 241834791, + 233124083, + 235788000, + 256420250, + 264680583, + 264429958, + 249323541, + 231824667, + 236639083 + ], + "mean_ns": 245854216.4, + "stddev_ns": 11774342.513211329, + "min_ns": 231824667, + "max_ns": 264680583, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_multiline_script", + "category": "large", + "iterations": 10, + "times_ns": [ + 231042, + 212167, + 207000, + 205125, + 200541, + 211542, + 202583, + 203250, + 202208, + 219625 + ], + "mean_ns": 209508.3, + "stddev_ns": 9075.187668031995, + "min_ns": 200541, + "max_ns": 231042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_multiline_script", + "category": "large", + "iterations": 10, + "times_ns": [ + 4077625, + 4226625, + 4300208, + 3925833, + 4315750, + 4041084, + 4382750, + 4041916, + 3970583, + 4815750 + ], + "mean_ns": 4209812.4, + "stddev_ns": 250749.1934117436, + "min_ns": 3925833, + "max_ns": 4815750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_pipeline_chain", + "category": "large", + "iterations": 10, + "times_ns": [ + 469875, + 465416, + 464583, + 462042, + 461709, + 464417, + 456458, + 456625, + 473625, + 464875 + ], + "mean_ns": 463962.5, + "stddev_ns": 5007.808707408861, + "min_ns": 456458, + "max_ns": 473625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_pipeline_chain", + "category": "large", + "iterations": 10, + "times_ns": [ + 6857417, + 6671208, + 6560833, + 6480333, + 7081416, + 6991125, + 6718083, + 6837625, + 6739167, + 6711333 + ], + "mean_ns": 6764854.0, + "stddev_ns": 173876.90466073982, + "min_ns": 6480333, + "max_ns": 7081416, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "large_assoc_array", + "category": "large", + "iterations": 10, + "times_ns": [ + 26000, + 23458, + 23375, + 22375, + 22666, + 22625, + 20958, + 20333, + 22375, + 22417 + ], + "mean_ns": 22658.2, + "stddev_ns": 1443.540425481739, + "min_ns": 20333, + "max_ns": 26000, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "large_assoc_array", + "category": "large", + "iterations": 10, + "times_ns": [ + 3828958, + 3725000, + 4912709, + 4423209, + 4415208, + 3796000, + 3715916, + 3984000, + 3950042, + 4201542 + ], + "mean_ns": 4095258.4, + "stddev_ns": 369373.8886641014, + "min_ns": 3715916, + "max_ns": 4912709, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "subshell_simple", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 16833, + 16000, + 16292, + 16875, + 16167, + 16500, + 16334, + 16417, + 16500, + 16417 + ], + "mean_ns": 16433.5, + "stddev_ns": 255.4953032836416, + "min_ns": 16000, + "max_ns": 16875, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "subshell_simple", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 4015792, + 4523709, + 4299750, + 4641917, + 4253042, + 4068500, + 4162250, + 4006542, + 4296708, + 4257250 + ], + "mean_ns": 4252546.0, + "stddev_ns": 196961.09868854814, + "min_ns": 4006542, + "max_ns": 4641917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "subshell_isolation", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 23334, + 22250, + 21375, + 20667, + 19208, + 18917, + 21250, + 21166, + 21125, + 21167 + ], + "mean_ns": 21045.9, + "stddev_ns": 1221.6474491439826, + "min_ns": 18917, + "max_ns": 23334, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "subshell_isolation", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 4092833, + 4225750, + 4675833, + 4139292, + 4761042, + 4531209, + 4126792, + 4404666, + 4324042, + 4437000 + ], + "mean_ns": 4371845.9, + "stddev_ns": 221407.02972193543, + "min_ns": 4092833, + "max_ns": 4761042, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "subshell_nested", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 27250, + 27833, + 27000, + 26250, + 26291, + 26667, + 33500, + 26542, + 26375, + 26834 + ], + "mean_ns": 27454.2, + "stddev_ns": 2067.5451047075126, + "min_ns": 26250, + "max_ns": 33500, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "subshell_nested", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 9123459, + 13370625, + 7466167, + 6534292, + 7182792, + 6129917, + 6511375, + 6440042, + 6098459, + 6405875 + ], + "mean_ns": 7526300.3, + "stddev_ns": 2127598.2223038753, + "min_ns": 6098459, + "max_ns": 13370625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "subshell_pipeline", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 21791, + 19708, + 19083, + 19042, + 19000, + 18375, + 18667, + 18333, + 18542, + 18417 + ], + "mean_ns": 19095.8, + "stddev_ns": 985.082615824683, + "min_ns": 18333, + "max_ns": 21791, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "subshell_pipeline", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 6073917, + 6189375, + 6178083, + 5887083, + 6433292, + 6295458, + 5948000, + 6134000, + 5993250, + 6106375 + ], + "mean_ns": 6123883.3, + "stddev_ns": 154718.5468055139, + "min_ns": 5887083, + "max_ns": 6433292, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "subshell_capture_loop", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 47458, + 45417, + 44375, + 43667, + 43458, + 43000, + 42584, + 43125, + 43084, + 42709 + ], + "mean_ns": 43887.7, + "stddev_ns": 1437.4016870728933, + "min_ns": 42584, + "max_ns": 47458, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "subshell_capture_loop", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 6906542, + 6547042, + 6569541, + 6636084, + 6563584, + 6400709, + 6923167, + 6686042, + 6334125, + 6609541 + ], + "mean_ns": 6617637.7, + "stddev_ns": 178761.06353121196, + "min_ns": 6334125, + "max_ns": 6923167, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "subshell_process_subst", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 31917, + 30333, + 29250, + 29000, + 28792, + 28708, + 28208, + 28500, + 28584, + 28084 + ], + "mean_ns": 29137.6, + "stddev_ns": 1103.4873991124682, + "min_ns": 28084, + "max_ns": 31917, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "subshell_process_subst", + "category": "subshell", + "iterations": 10, + "times_ns": [ + 4900417, + 4810375, + 4919250, + 4742500, + 4898542, + 4832958, + 4805417, + 4872042, + 4990625, + 4678125 + ], + "mean_ns": 4845025.1, + "stddev_ns": 86401.95955237358, + "min_ns": 4678125, + "max_ns": 4990625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "io_redirect_write", + "category": "io", + "iterations": 10, + "times_ns": [ + 33500, + 31292, + 30458, + 29833, + 29917, + 29541, + 29083, + 29042, + 29125, + 29125 + ], + "mean_ns": 30091.6, + "stddev_ns": 1325.9141902853291, + "min_ns": 29042, + "max_ns": 33500, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "io_redirect_write", + "category": "io", + "iterations": 10, + "times_ns": [ + 7117125, + 6784917, + 6670833, + 7514750, + 7103708, + 6486167, + 6860791, + 7015791, + 6684625, + 7061375 + ], + "mean_ns": 6930008.2, + "stddev_ns": 279632.86680853524, + "min_ns": 6486167, + "max_ns": 7514750, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "io_append", + "category": "io", + "iterations": 10, + "times_ns": [ + 38125, + 34000, + 36625, + 36334, + 36292, + 35875, + 35875, + 39167, + 36541, + 36833 + ], + "mean_ns": 36566.7, + "stddev_ns": 1301.5102804050377, + "min_ns": 34000, + "max_ns": 39167, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "io_append", + "category": "io", + "iterations": 10, + "times_ns": [ + 7167500, + 7305375, + 6658333, + 6951209, + 6831583, + 6816667, + 7104500, + 7097792, + 6690292, + 7159208 + ], + "mean_ns": 6978245.9, + "stddev_ns": 209623.39208611715, + "min_ns": 6658333, + "max_ns": 7305375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "io_dev_null", + "category": "io", + "iterations": 10, + "times_ns": [ + 21208, + 19917, + 18417, + 17375, + 16291, + 16292, + 16083, + 16250, + 18000, + 18125 + ], + "mean_ns": 17795.8, + "stddev_ns": 1634.1557330927797, + "min_ns": 16083, + "max_ns": 21208, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "io_dev_null", + "category": "io", + "iterations": 10, + "times_ns": [ + 5400375, + 4663833, + 5144667, + 4282125, + 5143042, + 4615750, + 4541125, + 4910375, + 4531333, + 5368292 + ], + "mean_ns": 4860091.7, + "stddev_ns": 368216.49360207916, + "min_ns": 4282125, + "max_ns": 5400375, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "io_stderr_redirect", + "category": "io", + "iterations": 10, + "times_ns": [ + 27541, + 19583, + 18833, + 18375, + 17792, + 17500, + 18042, + 18125, + 17584, + 18042 + ], + "mean_ns": 19141.7, + "stddev_ns": 2860.4504557848927, + "min_ns": 17500, + "max_ns": 27541, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "io_stderr_redirect", + "category": "io", + "iterations": 10, + "times_ns": [ + 5189541, + 5288042, + 4824542, + 5049458, + 5510500, + 4629750, + 5017500, + 4867625, + 4992417, + 5230166 + ], + "mean_ns": 5059954.1, + "stddev_ns": 241813.8208363823, + "min_ns": 4629750, + "max_ns": 5510500, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "io_read_lines", + "category": "io", + "iterations": 10, + "times_ns": [ + 42333, + 40750, + 42625, + 40250, + 39042, + 39375, + 39500, + 39125, + 38875, + 41750 + ], + "mean_ns": 40362.5, + "stddev_ns": 1352.6489012304708, + "min_ns": 38875, + "max_ns": 42625, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "io_read_lines", + "category": "io", + "iterations": 10, + "times_ns": [ + 5061916, + 4572833, + 5768708, + 4553000, + 4914917, + 5023083, + 4846708, + 4656000, + 5657667, + 5037625 + ], + "mean_ns": 5009245.7, + "stddev_ns": 394932.816954998, + "min_ns": 4553000, + "max_ns": 5768708, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bashkit", + "case_name": "io_multiline_heredoc", + "category": "io", + "iterations": 10, + "times_ns": [ + 27208, + 24625, + 23625, + 23167, + 22666, + 22958, + 22875, + 22792, + 22458, + 23000 + ], + "mean_ns": 23537.4, + "stddev_ns": 1354.4145746410145, + "min_ns": 22458, + "max_ns": 27208, + "errors": 0, + "error_messages": [], + "output_match": true + }, + { + "runner": "bash", + "case_name": "io_multiline_heredoc", + "category": "io", + "iterations": 10, + "times_ns": [ + 7111167, + 6933375, + 7324459, + 7105625, + 7372041, + 7240875, + 6902375, + 7325208, + 7070459, + 6898750 + ], + "mean_ns": 7128433.4, + "stddev_ns": 171819.11472254767, + "min_ns": 6898750, + "max_ns": 7372041, + "errors": 0, + "error_messages": [], + "output_match": true + } + ], + "summary": { + "total_cases": 96, + "runner_stats": { + "bash": { + "total_time_ms": 1004.8622964, + "avg_time_ms": 10.4673155875, + "error_count": 0, + "error_rate": 0.0, + "output_match_rate": 1.0 + }, + "bashkit": { + "total_time_ms": 20.1558197, + "avg_time_ms": 0.2099564552083333, + "error_count": 0, + "error_rate": 0.0, + "output_match_rate": 1.0 + } + } + } +} \ No newline at end of file diff --git a/crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.md b/crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.md new file mode 100644 index 000000000..5dc740a1b --- /dev/null +++ b/crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.md @@ -0,0 +1,308 @@ +# Bashkit Benchmark Report + +## System Information + +- **Moniker**: `mykhailosmac-macos-aarch64` +- **Hostname**: Mykhailos-Mac-mini.local +- **OS**: macos +- **Architecture**: aarch64 +- **CPUs**: 12 +- **Timestamp**: 1788629833 +- **Iterations**: 10 +- **Warmup**: 2 +- **Prewarm cases**: 3 + +- **bash executable**: `/opt/homebrew/bin/bash (GNU Bash 5.3.15(1)-release)` + +## Summary + +Benchmarked 96 cases across 2 runners. + +| Runner | Total Time (ms) | Avg/Case (ms) | Errors | Error Rate | Output Match | +|--------|-----------------|---------------|--------|------------|-------------| +| bashkit | 20.16 | 0.210 | 0 | 0.0% | 100.0% | +| bash | 1004.86 | 10.467 | 0 | 0.0% | 100.0% | + +## Performance Comparison + +**Bashkit is 49.9x faster** than bash on average. + +## Results by Category + +### Arithmetic + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| arith_basic | bashkit | 0.017 | ±0.001 | - | ✓ | +| arith_basic | bash | 3.885 | ±0.189 | - | ✓ | +| arith_complex | bashkit | 0.018 | ±0.001 | - | ✓ | +| arith_complex | bash | 3.669 | ±0.171 | - | ✓ | +| arith_variables | bashkit | 0.020 | ±0.001 | - | ✓ | +| arith_variables | bash | 3.650 | ±0.078 | - | ✓ | +| arith_increment | bashkit | 0.018 | ±0.001 | - | ✓ | +| arith_increment | bash | 4.742 | ±0.289 | - | ✓ | +| arith_modulo | bashkit | 0.017 | ±0.001 | - | ✓ | +| arith_modulo | bash | 4.759 | ±0.416 | - | ✓ | +| arith_loop_sum | bashkit | 0.038 | ±0.002 | - | ✓ | +| arith_loop_sum | bash | 7.080 | ±4.903 | - | ✓ | + +### Arrays + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| arr_create | bashkit | 0.018 | ±0.002 | - | ✓ | +| arr_create | bash | 4.080 | ±0.272 | - | ✓ | +| arr_all | bashkit | 0.018 | ±0.001 | - | ✓ | +| arr_all | bash | 3.889 | ±0.217 | - | ✓ | +| arr_length | bashkit | 0.018 | ±0.001 | - | ✓ | +| arr_length | bash | 3.755 | ±0.121 | - | ✓ | +| arr_iterate | bashkit | 0.023 | ±0.001 | - | ✓ | +| arr_iterate | bash | 4.048 | ±0.378 | - | ✓ | +| arr_slice | bashkit | 0.020 | ±0.001 | - | ✓ | +| arr_slice | bash | 4.457 | ±0.457 | - | ✓ | +| arr_assign_index | bashkit | 0.020 | ±0.001 | - | ✓ | +| arr_assign_index | bash | 4.110 | ±0.324 | - | ✓ | + +### Complex + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| complex_fibonacci | bashkit | 2.176 | ±0.087 | - | ✓ | +| complex_fibonacci | bash | 78.101 | ±5.523 | - | ✓ | +| complex_fibonacci_iter | bashkit | 0.065 | ±0.037 | - | ✓ | +| complex_fibonacci_iter | bash | 4.503 | ±0.162 | - | ✓ | +| complex_nested_subst | bashkit | 0.025 | ±0.002 | - | ✓ | +| complex_nested_subst | bash | 5.276 | ±0.266 | - | ✓ | +| complex_loop_compute | bashkit | 0.049 | ±0.001 | - | ✓ | +| complex_loop_compute | bash | 3.846 | ±0.095 | - | ✓ | +| complex_string_build | bashkit | 0.033 | ±0.013 | - | ✓ | +| complex_string_build | bash | 3.800 | ±0.175 | - | ✓ | +| complex_json_transform | bashkit | 0.238 | ±0.015 | - | ✓ | +| complex_json_transform | bash | 6.588 | ±0.281 | - | ✓ | +| complex_pipeline_text | bashkit | 0.066 | ±0.003 | - | ✓ | +| complex_pipeline_text | bash | 6.123 | ±0.255 | - | ✓ | + +### Control + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| ctrl_if_simple | bashkit | 0.019 | ±0.002 | - | ✓ | +| ctrl_if_simple | bash | 4.304 | ±0.264 | - | ✓ | +| ctrl_if_else | bashkit | 0.020 | ±0.001 | - | ✓ | +| ctrl_if_else | bash | 4.119 | ±0.201 | - | ✓ | +| ctrl_for_list | bashkit | 0.029 | ±0.001 | - | ✓ | +| ctrl_for_list | bash | 4.081 | ±0.274 | - | ✓ | +| ctrl_for_range | bashkit | 0.032 | ±0.002 | - | ✓ | +| ctrl_for_range | bash | 3.812 | ±0.138 | - | ✓ | +| ctrl_while | bashkit | 0.049 | ±0.001 | - | ✓ | +| ctrl_while | bash | 3.767 | ±0.143 | - | ✓ | +| ctrl_case | bashkit | 0.021 | ±0.001 | - | ✓ | +| ctrl_case | bash | 3.801 | ±0.201 | - | ✓ | +| ctrl_function | bashkit | 0.021 | ±0.002 | - | ✓ | +| ctrl_function | bash | 3.774 | ±0.158 | - | ✓ | +| ctrl_function_return | bashkit | 0.025 | ±0.001 | - | ✓ | +| ctrl_function_return | bash | 4.332 | ±0.142 | - | ✓ | +| ctrl_nested_loops | bashkit | 0.039 | ±0.001 | - | ✓ | +| ctrl_nested_loops | bash | 3.759 | ±0.154 | - | ✓ | + +### Io + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| io_redirect_write | bashkit | 0.030 | ±0.001 | - | ✓ | +| io_redirect_write | bash | 6.930 | ±0.280 | - | ✓ | +| io_append | bashkit | 0.037 | ±0.001 | - | ✓ | +| io_append | bash | 6.978 | ±0.210 | - | ✓ | +| io_dev_null | bashkit | 0.018 | ±0.002 | - | ✓ | +| io_dev_null | bash | 4.860 | ±0.368 | - | ✓ | +| io_stderr_redirect | bashkit | 0.019 | ±0.003 | - | ✓ | +| io_stderr_redirect | bash | 5.060 | ±0.242 | - | ✓ | +| io_read_lines | bashkit | 0.040 | ±0.001 | - | ✓ | +| io_read_lines | bash | 5.009 | ±0.395 | - | ✓ | +| io_multiline_heredoc | bashkit | 0.024 | ±0.001 | - | ✓ | +| io_multiline_heredoc | bash | 7.128 | ±0.172 | - | ✓ | + +### Large + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| large_loop_1000 | bashkit | 2.406 | ±0.052 | - | ✓ | +| large_loop_1000 | bash | 5.418 | ±0.147 | - | ✓ | +| large_string_append_100 | bashkit | 0.178 | ±0.003 | - | ✓ | +| large_string_append_100 | bash | 4.136 | ±0.179 | - | ✓ | +| large_array_fill_200 | bashkit | 0.741 | ±0.016 | - | ✓ | +| large_array_fill_200 | bash | 4.116 | ±0.170 | - | ✓ | +| large_nested_loops | bashkit | 1.083 | ±0.017 | - | ✓ | +| large_nested_loops | bash | 4.593 | ±0.150 | - | ✓ | +| large_fibonacci_12 | bashkit | 6.139 | ±0.176 | - | ✓ | +| large_fibonacci_12 | bash | 185.502 | ±3.336 | - | ✓ | +| large_function_calls_500 | bashkit | 2.966 | ±0.049 | - | ✓ | +| large_function_calls_500 | bash | 245.854 | ±11.774 | - | ✓ | +| large_multiline_script | bashkit | 0.210 | ±0.009 | - | ✓ | +| large_multiline_script | bash | 4.210 | ±0.251 | - | ✓ | +| large_pipeline_chain | bashkit | 0.464 | ±0.005 | - | ✓ | +| large_pipeline_chain | bash | 6.765 | ±0.174 | - | ✓ | +| large_assoc_array | bashkit | 0.023 | ±0.001 | - | ✓ | +| large_assoc_array | bash | 4.095 | ±0.369 | - | ✓ | + +### Pipes + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| pipe_simple | bashkit | 0.024 | ±0.002 | - | ✓ | +| pipe_simple | bash | 6.336 | ±0.376 | - | ✓ | +| pipe_multi | bashkit | 0.032 | ±0.001 | - | ✓ | +| pipe_multi | bash | 6.601 | ±0.315 | - | ✓ | +| pipe_command_subst | bashkit | 0.020 | ±0.001 | - | ✓ | +| pipe_command_subst | bash | 4.583 | ±0.198 | - | ✓ | +| pipe_heredoc | bashkit | 0.021 | ±0.001 | - | ✓ | +| pipe_heredoc | bash | 5.435 | ±0.204 | - | ✓ | +| pipe_herestring | bashkit | 0.020 | ±0.001 | - | ✓ | +| pipe_herestring | bash | 7.253 | ±0.318 | - | ✓ | +| pipe_discard | bashkit | 0.020 | ±0.001 | - | ✓ | +| pipe_discard | bash | 5.460 | ±0.509 | - | ✓ | + +### Startup + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| startup_empty | bashkit | 0.025 | ±0.013 | - | ✓ | +| startup_empty | bash | 4.451 | ±0.248 | - | ✓ | +| startup_true | bashkit | 0.016 | ±0.001 | - | ✓ | +| startup_true | bash | 5.086 | ±0.748 | - | ✓ | +| startup_echo | bashkit | 0.017 | ±0.001 | - | ✓ | +| startup_echo | bash | 6.145 | ±1.645 | - | ✓ | +| startup_exit | bashkit | 0.016 | ±0.001 | - | ✓ | +| startup_exit | bash | 4.212 | ±0.219 | - | ✓ | + +### Strings + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| str_concat | bashkit | 0.018 | ±0.001 | - | ✓ | +| str_concat | bash | 3.773 | ±0.145 | - | ✓ | +| str_printf | bashkit | 0.019 | ±0.004 | - | ✓ | +| str_printf | bash | 3.752 | ±0.146 | - | ✓ | +| str_printf_pad | bashkit | 0.017 | ±0.001 | - | ✓ | +| str_printf_pad | bash | 3.815 | ±0.315 | - | ✓ | +| str_echo_escape | bashkit | 0.023 | ±0.016 | - | ✓ | +| str_echo_escape | bash | 4.852 | ±0.319 | - | ✓ | +| str_prefix_strip | bashkit | 0.019 | ±0.001 | - | ✓ | +| str_prefix_strip | bash | 5.303 | ±0.289 | - | ✓ | +| str_suffix_strip | bashkit | 0.024 | ±0.009 | - | ✓ | +| str_suffix_strip | bash | 5.150 | ±0.336 | - | ✓ | +| str_uppercase | bashkit | 0.018 | ±0.003 | - | ✓ | +| str_uppercase | bash | 4.578 | ±0.256 | - | ✓ | +| str_lowercase | bashkit | 0.018 | ±0.001 | - | ✓ | +| str_lowercase | bash | 4.178 | ±0.260 | - | ✓ | + +### Subshell + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| subshell_simple | bashkit | 0.016 | ±0.000 | - | ✓ | +| subshell_simple | bash | 4.253 | ±0.197 | - | ✓ | +| subshell_isolation | bashkit | 0.021 | ±0.001 | - | ✓ | +| subshell_isolation | bash | 4.372 | ±0.221 | - | ✓ | +| subshell_nested | bashkit | 0.027 | ±0.002 | - | ✓ | +| subshell_nested | bash | 7.526 | ±2.128 | - | ✓ | +| subshell_pipeline | bashkit | 0.019 | ±0.001 | - | ✓ | +| subshell_pipeline | bash | 6.124 | ±0.155 | - | ✓ | +| subshell_capture_loop | bashkit | 0.044 | ±0.001 | - | ✓ | +| subshell_capture_loop | bash | 6.618 | ±0.179 | - | ✓ | +| subshell_process_subst | bashkit | 0.029 | ±0.001 | - | ✓ | +| subshell_process_subst | bash | 4.845 | ±0.086 | - | ✓ | + +### Tools + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| tool_grep_simple | bashkit | 0.022 | ±0.001 | - | ✓ | +| tool_grep_simple | bash | 7.946 | ±0.617 | - | ✓ | +| tool_grep_case | bashkit | 0.080 | ±0.026 | - | ✓ | +| tool_grep_case | bash | 7.619 | ±0.454 | - | ✓ | +| tool_grep_count | bashkit | 0.023 | ±0.003 | - | ✓ | +| tool_grep_count | bash | 7.224 | ±0.281 | - | ✓ | +| tool_grep_invert | bashkit | 0.022 | ±0.001 | - | ✓ | +| tool_grep_invert | bash | 6.839 | ±0.242 | - | ✓ | +| tool_grep_regex | bashkit | 0.032 | ±0.002 | - | ✓ | +| tool_grep_regex | bash | 6.877 | ±0.383 | - | ✓ | +| tool_sed_replace | bashkit | 0.056 | ±0.003 | - | ✓ | +| tool_sed_replace | bash | 6.468 | ±0.330 | - | ✓ | +| tool_sed_global | bashkit | 0.070 | ±0.048 | - | ✓ | +| tool_sed_global | bash | 6.950 | ±0.369 | - | ✓ | +| tool_sed_delete | bashkit | 0.021 | ±0.001 | - | ✓ | +| tool_sed_delete | bash | 6.849 | ±0.489 | - | ✓ | +| tool_sed_lines | bashkit | 0.019 | ±0.001 | - | ✓ | +| tool_sed_lines | bash | 6.717 | ±0.307 | - | ✓ | +| tool_sed_backrefs | bashkit | 0.075 | ±0.004 | - | ✓ | +| tool_sed_backrefs | bash | 6.860 | ±0.308 | - | ✓ | +| tool_awk_print | bashkit | 0.020 | ±0.002 | - | ✓ | +| tool_awk_print | bash | 6.601 | ±0.401 | - | ✓ | +| tool_awk_sum | bashkit | 0.023 | ±0.001 | - | ✓ | +| tool_awk_sum | bash | 5.832 | ±0.238 | - | ✓ | +| tool_awk_pattern | bashkit | 0.031 | ±0.002 | - | ✓ | +| tool_awk_pattern | bash | 6.592 | ±0.746 | - | ✓ | +| tool_awk_fieldsep | bashkit | 0.021 | ±0.001 | - | ✓ | +| tool_awk_fieldsep | bash | 7.455 | ±0.399 | - | ✓ | +| tool_awk_nf | bashkit | 0.024 | ±0.007 | - | ✓ | +| tool_awk_nf | bash | 7.489 | ±0.266 | - | ✓ | +| tool_awk_compute | bashkit | 0.021 | ±0.001 | - | ✓ | +| tool_awk_compute | bash | 7.186 | ±0.271 | - | ✓ | +| tool_jq_identity | bashkit | 0.289 | ±0.087 | - | ✓ | +| tool_jq_identity | bash | 7.583 | ±0.476 | - | ✓ | +| tool_jq_field | bashkit | 0.260 | ±0.066 | - | ✓ | +| tool_jq_field | bash | 8.001 | ±0.323 | - | ✓ | +| tool_jq_array | bashkit | 0.324 | ±0.122 | - | ✓ | +| tool_jq_array | bash | 8.071 | ±0.232 | - | ✓ | +| tool_jq_filter | bashkit | 0.230 | ±0.010 | - | ✓ | +| tool_jq_filter | bash | 7.965 | ±0.399 | - | ✓ | +| tool_jq_map | bashkit | 0.287 | ±0.038 | - | ✓ | +| tool_jq_map | bash | 7.842 | ±0.203 | - | ✓ | + +### Variables + +| Benchmark | Runner | Mean (ms) | StdDev | Errors | Match | +|-----------|--------|-----------|--------|--------|-------| +| var_assign_simple | bashkit | 0.019 | ±0.001 | - | ✓ | +| var_assign_simple | bash | 4.283 | ±0.256 | - | ✓ | +| var_assign_many | bashkit | 0.029 | ±0.004 | - | ✓ | +| var_assign_many | bash | 4.241 | ±0.347 | - | ✓ | +| var_default | bashkit | 0.017 | ±0.001 | - | ✓ | +| var_default | bash | 4.130 | ±0.178 | - | ✓ | +| var_length | bashkit | 0.017 | ±0.001 | - | ✓ | +| var_length | bash | 4.207 | ±0.230 | - | ✓ | +| var_substring | bashkit | 0.018 | ±0.001 | - | ✓ | +| var_substring | bash | 4.062 | ±0.198 | - | ✓ | +| var_replace | bashkit | 0.019 | ±0.001 | - | ✓ | +| var_replace | bash | 3.855 | ±0.131 | - | ✓ | +| var_nested | bashkit | 0.019 | ±0.001 | - | ✓ | +| var_nested | bash | 3.849 | ±0.181 | - | ✓ | +| var_export | bashkit | 0.019 | ±0.001 | - | ✓ | +| var_export | bash | 3.803 | ±0.188 | - | ✓ | + +## Runner Descriptions + +| Runner | Type | Description | +|--------|------|-------------| +| bashkit | in-process | Rust library call, no fork/exec | +| bashkit-cli | subprocess | bashkit binary, new process per run | +| bashkit-js | persistent child | Node.js + @everruns/bashkit, warm interpreter | +| bashkit-py | persistent child | Python + bashkit package, warm interpreter | +| bash | subprocess | PATH-selected Bash >=4, new process per run | +| gbash | subprocess | gbash binary (Go), new process per run | +| gbash-server | persistent child | gbash JSON-RPC server, warm interpreter | +| just-bash | subprocess | just-bash CLI, new process per run | +| just-bash-inproc | persistent child | Node.js + just-bash library, warm interpreter | + +## Assumptions & Notes + +- Times measured in nanoseconds, displayed in milliseconds +- Prewarm phase runs first few cases to warm up JIT/compilation +- Per-benchmark warmup iterations excluded from timing +- Output match compares against bash output when available +- Errors include execution failures and exit code mismatches +- In-process: interpreter runs inside the benchmark process +- Subprocess: new process spawned per benchmark run +- Persistent child: long-lived child process, amortizes startup cost + diff --git a/crates/bashkit-bench/src/main.rs b/crates/bashkit-bench/src/main.rs index 0c52826d6..8270831fc 100644 --- a/crates/bashkit-bench/src/main.rs +++ b/crates/bashkit-bench/src/main.rs @@ -171,6 +171,8 @@ pub struct BenchReport { pub warmup: usize, pub prewarm_cases: usize, pub runners: Vec, + #[serde(default)] + pub runner_versions: HashMap, pub results: Vec, pub summary: BenchSummary, } @@ -276,6 +278,7 @@ async fn main() -> Result<()> { }; match result { Ok(r) => runners.push(r), + Err(e) if *name == "bash" => return Err(e), Err(e) => eprintln!("{}: {} not available: {}", "Warning".yellow(), name, e), } } @@ -379,7 +382,15 @@ async fn main() -> Result<()> { } // Generate report - let report = generate_report(&results, &args, &runner_names, &system_info, prewarm_count); + let mut report = generate_report(&results, &args, &runner_names, &system_info, prewarm_count); + for runner in &runners { + if let Runner::NativeBash(path) = runner { + let version = runners::bash_version(path).await?; + report + .runner_versions + .insert("bash".into(), format!("{path} (GNU Bash {version})")); + } + } // Print results table println!("\n{}", "Results:".bold()); @@ -577,6 +588,7 @@ fn generate_report( warmup: args.warmup, prewarm_cases: prewarm_count, runners: runner_names.iter().map(|s| s.to_string()).collect(), + runner_versions: HashMap::new(), results: results.to_vec(), summary: BenchSummary { total_cases: unique_cases.len(), @@ -604,6 +616,11 @@ fn generate_markdown_report(report: &BenchReport) -> String { md.push_str(&format!("- **Prewarm cases**: {}\n", report.prewarm_cases)); md.push('\n'); + for (runner, version) in &report.runner_versions { + md.push_str(&format!("- **{runner} executable**: `{version}`\n")); + } + md.push('\n'); + // Summary md.push_str("## Summary\n\n"); md.push_str(&format!( @@ -711,7 +728,7 @@ fn generate_markdown_report(report: &BenchReport) -> String { "| bashkit-js | persistent child | Node.js + @everruns/bashkit, warm interpreter |\n", ); md.push_str("| bashkit-py | persistent child | Python + bashkit package, warm interpreter |\n"); - md.push_str("| bash | subprocess | /bin/bash, new process per run |\n"); + md.push_str("| bash | subprocess | PATH-selected Bash >=4, new process per run |\n"); md.push_str("| gbash | subprocess | gbash binary (Go), new process per run |\n"); md.push_str("| gbash-server | persistent child | gbash JSON-RPC server, warm interpreter |\n"); md.push_str("| just-bash | subprocess | just-bash CLI, new process per run |\n"); diff --git a/crates/bashkit-bench/src/runners.rs b/crates/bashkit-bench/src/runners.rs index 4bbbb740e..b4d5b9d61 100644 --- a/crates/bashkit-bench/src/runners.rs +++ b/crates/bashkit-bench/src/runners.rs @@ -7,7 +7,7 @@ // - bashkit-cli: out-of-process via bashkit binary (subprocess per run) // - bashkit-js: in-process via Node.js + @everruns/bashkit (persistent child) // - bashkit-py: in-process via Python + bashkit package (persistent child) -// - bash: out-of-process via /bin/bash (subprocess per run) +// - bash: PATH-selected Bash >=4 (subprocess per run; macOS system Bash is too old) // - gbash: out-of-process via gbash binary (subprocess per run) // - just-bash: out-of-process via just-bash CLI (subprocess per run) // - just-bash-inproc: in-process via Node.js + just-bash library (persistent child) @@ -164,27 +164,51 @@ pub struct BashRunner; impl BashRunner { pub async fn create() -> Result { - let path = which_bash().await?; + let path = which_bash(&std::env::var_os("PATH").unwrap_or_default()).await?; Ok(Runner::NativeBash(path)) } } -async fn which_bash() -> Result { - for path in &["/bin/bash", "/usr/bin/bash", "/usr/local/bin/bash"] { - if Path::new(path).exists() { - return Ok(path.to_string()); +async fn which_bash(search_path: &std::ffi::OsStr) -> Result { + // Honor the user's shell selection; /bin/bash is still 3.2 on macOS. + for directory in std::env::split_paths(search_path) { + let candidate = directory.join(if cfg!(windows) { "bash.exe" } else { "bash" }); + if !candidate.is_file() { + continue; } - } - - let output = Command::new("which").arg("bash").output().await?; - if output.status.success() { - let path = String::from_utf8_lossy(&output.stdout).trim().to_string(); - if !path.is_empty() { - return Ok(path); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if candidate.metadata()?.permissions().mode() & 0o111 == 0 { + continue; + } } + let path = std::path::absolute(candidate)? + .to_string_lossy() + .into_owned(); + bash_version(&path).await?; + return Ok(path); } + anyhow::bail!("bash not found on PATH; install Bash >=4 for the benchmark oracle") +} - anyhow::bail!("bash not found") +pub async fn bash_version(path: &str) -> Result { + let output = Command::new(path) + .args(["-c", "printf '%s' \"$BASH_VERSION\""]) + .output() + .await + .with_context(|| format!("cannot execute benchmark Bash {path}"))?; + let version = String::from_utf8_lossy(&output.stdout).trim().to_string(); + let major = version + .split('.') + .next() + .and_then(|v| v.parse::().ok()); + if !output.status.success() || !matches!(major, Some(4..)) { + anyhow::bail!( + "benchmark oracle requires Bash >=4 (case conversion and associative arrays); {path} reports {version:?}. Put a supported Bash first on PATH" + ); + } + Ok(version) } // === Gbash (out-of-process) === @@ -471,9 +495,46 @@ fn scripts_dir() -> PathBuf { #[cfg(test)] mod tests { + #[cfg(unix)] + use super::which_bash; use super::{run_subprocess, subprocess_program}; use std::path::PathBuf; + #[cfg(unix)] + #[tokio::test] + async fn bash_selection_uses_path_before_system_shell() { + let directory = tempfile::tempdir().unwrap(); + let shell = directory.path().join("bash"); + std::fs::write(&shell, "#!/bin/sh\nprintf '5.3.0\\n'\n").unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + std::fs::set_permissions(&shell, std::fs::Permissions::from_mode(0o755)).unwrap(); + } + assert_eq!( + which_bash(directory.path().as_os_str()).await.unwrap(), + shell.to_str().unwrap() + ); + } + + #[cfg(unix)] + #[tokio::test] + async fn bash_selection_rejects_missing_and_old_oracles() { + let directory = tempfile::tempdir().unwrap(); + let error = which_bash(directory.path().as_os_str()).await.unwrap_err(); + assert!(error.to_string().contains("bash not found on PATH")); + let shell = directory.path().join("bash"); + std::fs::write(&shell, "#!/bin/sh\nprintf '3.2.57'\n").unwrap(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + std::fs::set_permissions(&shell, std::fs::Permissions::from_mode(0o755)).unwrap(); + } + let error = which_bash(directory.path().as_os_str()).await.unwrap_err(); + assert!(error.to_string().contains("requires Bash >=4")); + assert!(error.to_string().contains("3.2.57")); + } + #[test] fn subprocess_program_resolves_relative_paths_before_cwd_isolation() { let cwd = std::env::current_dir().unwrap(); diff --git a/crates/bashkit-js/__test__/ai-adapters.spec.ts b/crates/bashkit-js/__test__/ai-adapters.spec.ts index 70ccd5a0b..a29ccb0d9 100644 --- a/crates/bashkit-js/__test__/ai-adapters.spec.ts +++ b/crates/bashkit-js/__test__/ai-adapters.spec.ts @@ -350,3 +350,15 @@ test("openai: sanitizeOutput re-caps length after escaping (#1867)", async (t) = t.true(result.content.startsWith(""), "outer tags intact"); t.true(result.content.endsWith(""), "outer tags intact"); }); + +test("anthropic: sanitized output is capped without splitting entities", async (t) => { + const adapter = anthropicBashTool({ sanitizeOutput: true, maxOutputLength: 21 }); + const result = await adapter.handler({ + type: "tool_use", id: "escaped-cap", name: "bash", + input: { commands: "printf '%s' '&&&&&&&&&&&&&&&&&&&&&&&&'" }, + }); + t.true(result.content.startsWith("\n")); + t.true(result.content.endsWith("\n")); + const inner = result.content.slice("\n".length, -"\n".length); + t.is(inner, "&".repeat(4) + "\n[truncated]"); +}); diff --git a/crates/bashkit-js/__test__/custom-builtins.spec.ts b/crates/bashkit-js/__test__/custom-builtins.spec.ts index 3eb4c5681..49e045679 100644 --- a/crates/bashkit-js/__test__/custom-builtins.spec.ts +++ b/crates/bashkit-js/__test__/custom-builtins.spec.ts @@ -454,3 +454,28 @@ test("ctx.fs respects files mounted at construction", async (t) => { t.is(result.exitCode, 0); t.is(result.stdout, '{"ok":true}'); }); + +for (const keyed of [false, true]) { + test(`BashTool snapshot constructor registers supplied callbacks (keyed=${keyed})`, async (t) => { + const key = Buffer.alloc(32, 7); + const source = new BashTool(); + await source.execute("echo restored > /tmp/state.txt"); + const snapshot = keyed + ? source.snapshotKeyed(key) + : source.snapshot({ hmacKey: key }); + const options = { + customBuiltins: { + greet: async (ctx: BuiltinContext) => `hello ${ctx.argv[0]}\n`, + }, + }; + const restored = keyed + ? BashTool.fromSnapshotKeyed(snapshot, key, options) + : BashTool.fromSnapshot(snapshot, options, { hmacKey: key }); + t.is( + (await restored.execute("greet Alice; cat /tmp/state.txt")).stdout, + "hello Alice\nrestored\n", + ); + restored.reset(); + t.is((await restored.execute("greet Bob")).stdout, "hello Bob\n"); + }); +} diff --git a/crates/bashkit-js/anthropic.ts b/crates/bashkit-js/anthropic.ts index 69c197a59..167abca58 100644 --- a/crates/bashkit-js/anthropic.ts +++ b/crates/bashkit-js/anthropic.ts @@ -145,10 +145,16 @@ function formatOutput( output = output.slice(0, maxOutputLength) + "\n[truncated]"; } if (sanitize) { - const escaped = output + let escaped = output .replace(/&/g, "&") .replace(//g, ">"); + // Bound expanded XML content and retain complete entities, as in the OpenAI adapter. + if (escaped.length > maxOutputLength) { + escaped = + escaped.slice(0, maxOutputLength).replace(/&[^;]{0,4}$/, "") + + "\n[truncated]"; + } output = `\n${escaped}\n`; } return output; diff --git a/crates/bashkit-js/package.json b/crates/bashkit-js/package.json index 0063f5602..92dc7efdb 100644 --- a/crates/bashkit-js/package.json +++ b/crates/bashkit-js/package.json @@ -102,15 +102,15 @@ } }, "devDependencies": { - "@langchain/core": "^1.2.8", - "@napi-rs/cli": "^3.8.6", - "@types/node": "^26.2.0", + "@langchain/core": "^1.2.9", + "@napi-rs/cli": "^3.9.0", + "@types/node": "^26.4.1", "ava": "^8.0.1", - "oxlint": "^1.78.0", + "oxlint": "^1.81.0", "prettier": "^3.9.6", - "tsx": "^4.23.12", + "tsx": "^4.23.13", "typescript": "^7.0.2", - "zod": "^4.4.3" + "zod": "^4.5.4" }, "overrides": { "langsmith": "^0.6.0", diff --git a/crates/bashkit-js/pnpm-lock.yaml b/crates/bashkit-js/pnpm-lock.yaml index 194b30dd3..29ecd3780 100644 --- a/crates/bashkit-js/pnpm-lock.yaml +++ b/crates/bashkit-js/pnpm-lock.yaml @@ -20,32 +20,32 @@ importers: .: devDependencies: '@langchain/core': - specifier: ^1.2.8 + specifier: ^1.2.9 version: 1.2.9 '@napi-rs/cli': - specifier: ^3.8.6 - version: 3.8.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@26.4.0)(emnapi@1.10.0) + specifier: ^3.9.0 + version: 3.9.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@26.4.1)(emnapi@1.10.0) '@types/node': - specifier: ^26.2.0 - version: 26.4.0 + specifier: ^26.4.1 + version: 26.4.1 ava: specifier: ^8.0.1 version: 8.0.1 oxlint: - specifier: ^1.78.0 - version: 1.80.0 + specifier: ^1.81.0 + version: 1.81.0 prettier: specifier: ^3.9.6 version: 3.9.6 tsx: - specifier: ^4.23.12 - version: 4.23.12 + specifier: ^4.23.13 + version: 4.23.13 typescript: specifier: ^7.0.2 version: 7.0.2 zod: - specifier: ^4.4.3 - version: 4.4.3 + specifier: ^4.5.4 + version: 4.5.4 optionalDependencies: '@napi-rs/wasm-runtime': specifier: ^1.2.3 @@ -238,8 +238,8 @@ packages: resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/checkbox@5.2.1': - resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + '@inquirer/checkbox@5.2.3': + resolution: {integrity: sha512-XEYX2WA8SBkLPczL6/yXPHLPCvDoptmh9v56Cy05BSV1Smk1vWy19bTC4qJBuIffw7+6l4CcaYYzGqG60RfW1g==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -247,8 +247,8 @@ packages: '@types/node': optional: true - '@inquirer/confirm@6.1.1': - resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + '@inquirer/confirm@6.3.0': + resolution: {integrity: sha512-pZHXJImFtERmSNMBHcjwuz8Ck5vEFEYNUZnwbb8aJpjHv/TwGuFErNxF2Hp8+V+pNJs2EYPMlyWscvFEqO9jOQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -256,8 +256,8 @@ packages: '@types/node': optional: true - '@inquirer/core@11.2.1': - resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + '@inquirer/core@12.0.1': + resolution: {integrity: sha512-JMD5Jy/ScL5TZE18m83Nw25HjqGFLoWXwnEkW7IdwwAhZpB9Bus55/WU7zn3UqR1MOCjjTQOIYpiD4vjWA3LPw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -265,8 +265,8 @@ packages: '@types/node': optional: true - '@inquirer/editor@5.2.2': - resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==} + '@inquirer/editor@5.3.1': + resolution: {integrity: sha512-y43COoyVUjPWIobn2Qep/uI1drPS78aaZZZ9kVi94Tyu/GuW2N8d8Q4rifJXGAXCEAXCPTTMjD8gC1HyvM5ukA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -274,8 +274,8 @@ packages: '@types/node': optional: true - '@inquirer/expand@5.1.1': - resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + '@inquirer/expand@5.1.3': + resolution: {integrity: sha512-3NQJiXNJ/aj9wiAsr7pECdp5Qe9J0X9YUJCKsaFXS+ddOxfL6J4AIl3w3T4Gq3kK0WsQY5GMoDokK5X94m6lHw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -283,8 +283,8 @@ packages: '@types/node': optional: true - '@inquirer/external-editor@3.0.3': - resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==} + '@inquirer/external-editor@3.0.4': + resolution: {integrity: sha512-tZbbaK2ovq6vlrRBNQvjrypmrED/p5x2ncIHQ79cD55tei3dD96v5glMMA+6tiq7K104i/25DVYKWVPJuV6ptA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -292,12 +292,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@2.0.7': - resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + '@inquirer/figures@2.0.8': + resolution: {integrity: sha512-tApbon79GM9ry56ja/Ud3SY2CL4TQsao9fIwDQbgTeNY55025GdMzQ2+UdegV/lx51VNGUB59M0v0nMpybYY4Q==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/input@5.1.2': - resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + '@inquirer/input@5.1.4': + resolution: {integrity: sha512-3xQkQrOvgOzpSN2ciTVdRDlg1FWMCA8l+0KfB6SNlILoTCGzJTzO/gc0Rwjcb3usuGyKdaGtI6OiyMdeMeLWkg==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -305,8 +305,8 @@ packages: '@types/node': optional: true - '@inquirer/number@4.1.1': - resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + '@inquirer/number@4.2.1': + resolution: {integrity: sha512-5KaqwZNLRpUuWcoCrYghPP9TMaXL5v2Sk4xqePM7RCVegcJStoXdWibio60YIC1bec+z1fCyb67N6XPJIkZtGA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -314,8 +314,8 @@ packages: '@types/node': optional: true - '@inquirer/password@5.1.1': - resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + '@inquirer/password@5.2.0': + resolution: {integrity: sha512-CvVcW09emkBESEOW+4R8CjLNkP3fB3XrjeL8CDvfpjgrJN+V9oerXmJAXXM3l+4xqYPD5Yaujzy/Ph0PLOdDuA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -323,8 +323,8 @@ packages: '@types/node': optional: true - '@inquirer/prompts@8.5.2': - resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==} + '@inquirer/prompts@8.7.0': + resolution: {integrity: sha512-yQwBMYvpJ6jqrXtKiOwRD5XezjJoyt3VQvIyjsr5Arqb519nfIohOQymWVJ8/vEgg8xtZerrCsqlSaqt/LPC9A==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -332,8 +332,8 @@ packages: '@types/node': optional: true - '@inquirer/rawlist@5.3.1': - resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + '@inquirer/rawlist@5.3.3': + resolution: {integrity: sha512-Mu7WrtmDLaXBDEyrRLS70SZgX9ZSm4Up1w0ZxiH8C1OOp9oaVCn2k8q3QGgmlnhsKYUhuaU3zFWhAP6wxkVIMA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -341,8 +341,8 @@ packages: '@types/node': optional: true - '@inquirer/search@4.2.1': - resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + '@inquirer/search@4.3.1': + resolution: {integrity: sha512-0VWOvsHWI0rPj6CG70MoP4oXNCB6adcyN8bVFZXnh11eLDdPIK2f2XCmva78acPPDfJisfab7qNakwBh7hdBXw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -350,8 +350,8 @@ packages: '@types/node': optional: true - '@inquirer/select@5.2.1': - resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + '@inquirer/select@5.2.3': + resolution: {integrity: sha512-KuRTodDa6xBXX2noIpjuitpX/QT7Sfav7dIZ/OfUY54Hxg95nrGoshSzxx6Ey7qqLbImKdiGkSDt7KjPXjQgmA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -359,8 +359,8 @@ packages: '@types/node': optional: true - '@inquirer/type@4.0.7': - resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + '@inquirer/type@4.1.0': + resolution: {integrity: sha512-FMiJpuHUG3Dk0ex+UIXkre7i+i4OcwHWk9YdcVtZHFwb/r2rnrU2ipTCNAB7A+QOP0ryzIcqOfy76fRyyvOEAw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -381,8 +381,8 @@ packages: engines: {node: '>=18'} hasBin: true - '@napi-rs/cli@3.8.6': - resolution: {integrity: sha512-FnJ9fghsV9Q4zh2aJGPSvQiUlJRC27B6KhzAXcIW2rlSD8keak3mhXw4tJYa3KJkP9whETfsPwqp/DJRnQg5ng==} + '@napi-rs/cli@3.9.0': + resolution: {integrity: sha512-ZlerYOCLgVdaqbVDt8+uQxmU9j8bKMVFcYo6zRHJJHTO9jN5060+y953aIVc/0zkVSKq+fHjDAtTGOrbxpeZCw==} engines: {node: ^20.17.0 || ^22.13.0 || >= 23.5.0} hasBin: true peerDependencies: @@ -757,23 +757,23 @@ packages: resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} engines: {node: '>= 20'} - '@octokit/core@7.0.7': - resolution: {integrity: sha512-DcB0M3KFgr9ECI328lhBMVsyFT2DnmNucSBTqEN3exyNKUzkkpUSCHmTRcunF41Eou2TIQKW4seewri8ON9bSA==} + '@octokit/core@7.0.8': + resolution: {integrity: sha512-L7y8eYc+AwxGr2PWI4WFt1VG4TiJ66c26BD16mXpYIlXxG0SMigM1+m4aTSlYyBr5BlQsGAlz8uDCoZN4SEMcg==} engines: {node: '>= 20'} - '@octokit/endpoint@11.0.4': - resolution: {integrity: sha512-f1cOWoHPmxryJFknxbtDdjODWfV8A9tc8Aae6ermXPNgHFZ/x91AtHIz4gicEjL8hkJiip+u21QHJORfBv/qiA==} + '@octokit/endpoint@11.0.5': + resolution: {integrity: sha512-iXa654H3yFafF/ieHkukfbgWo2rmXD2ceD0ZOtrPhw1bc3FDch1d9N/TNs0FQ1/cIbwb7kspUX8jzIs8nzb9DQ==} engines: {node: '>= 20'} - '@octokit/graphql@9.0.4': - resolution: {integrity: sha512-5s15CCiY8XXQ+FG+b1YQcl6Z2FA++nwAz/tg2VUrTmnMncP+2nnGUEYANImdnxsA2Fnq+Mbl7hDjUTw7cFAwcg==} + '@octokit/graphql@9.0.5': + resolution: {integrity: sha512-bt/hm03LeU6Vy7FwTrkkC9p3XGT/lBwClglMqxBSe5/q0E5CdJTXeAqEI0vlw89/LF/G6tryTIH8HirZ3prMVg==} engines: {node: '>= 20'} '@octokit/openapi-types@27.0.0': resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} - '@octokit/openapi-types@28.0.0': - resolution: {integrity: sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==} + '@octokit/openapi-types@29.0.1': + resolution: {integrity: sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==} '@octokit/plugin-paginate-rest@14.0.0': resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} @@ -793,12 +793,12 @@ packages: peerDependencies: '@octokit/core': '>=6' - '@octokit/request-error@7.1.1': - resolution: {integrity: sha512-+eaY7G2VVpSf2pc5Gn1+mph837V/d/TYTJAgWL9Tb0ogGYcpN3IlAVFgjL+Vv93F/sevrxkvsYCedtpLdcFLzA==} + '@octokit/request-error@7.1.2': + resolution: {integrity: sha512-XZRuT3xZ84D3gYErI1DZvhJ33dCWVV6uzBtWkaBB4TvA/L6eOeTZodxLFVB44bBEEo3vEx7y00UfX1tBLrtLRg==} engines: {node: '>= 20'} - '@octokit/request@10.0.14': - resolution: {integrity: sha512-bgWgiSfFS689/AxQDarU+b3Qu1FYewfVr5vI/jhV84s7GQ3C2OsdRxukGGYKB37MCgwcS50UrfBLlHzhiG13sw==} + '@octokit/request@10.0.16': + resolution: {integrity: sha512-A0zWGjHzISIb+9ccG8s0dq7LKO5zVpJLRICjgUb+sJxEWqn8RUHB1rD3AE51+PECvXHIxqZ1VVvs4fHTSD9nUQ==} engines: {node: '>= 20'} '@octokit/rest@22.0.1': @@ -808,127 +808,127 @@ packages: '@octokit/types@16.0.0': resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} - '@octokit/types@17.0.0': - resolution: {integrity: sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==} + '@octokit/types@18.0.0': + resolution: {integrity: sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==} - '@oxlint/binding-android-arm-eabi@1.80.0': - resolution: {integrity: sha512-RM3Plj+biQpxa5d1GOOX6ciDlcUROmm4OZ/pLTpitkQt2mJv4jhtY4cbgaetOm5UKWZe05/TGQ6o1Vl8EOHkrA==} + '@oxlint/binding-android-arm-eabi@1.81.0': + resolution: {integrity: sha512-IcCRsXiedJoJopY6mpZUBEeVFsUrutmrG7dZ87zMuKJlhg70Ora9bBl1WcCxZQtyI10YpnVdEso5oCg7YcfSHw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.80.0': - resolution: {integrity: sha512-YlO5JEf0Yr2bUUlu8O8daVcUxtcGGbcSmyV7E7nSbJbfAdxTE0PFPwgnIlw7wXJaTYjb+qs5hI5q3jxUkI7cAw==} + '@oxlint/binding-android-arm64@1.81.0': + resolution: {integrity: sha512-GRrIPyTGVhx3L3h+0T5xT2A0jFAcdPv4+IfuXpGDLIdl6XeYhgg/zw72A5ILZoUgRqZuM8F1y+V/gfDriXSxzQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.80.0': - resolution: {integrity: sha512-BULDOyO3AhsmdWfQeIUCykDt3dd7XZBGLhp1eIh56skRv01O+cNjNPwXMIbeW1x4+pxcln5if72wcRgViVo7PA==} + '@oxlint/binding-darwin-arm64@1.81.0': + resolution: {integrity: sha512-qNQ9tXRgLuKbqSV1S2h9h4KPHjbovO7RRR2/enUOtHzTkFZ7B9X5zqqHJua8dRyc7dBy7Aoyq5pqTSLFVcAzGQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.80.0': - resolution: {integrity: sha512-YJ4JzLw7N5TDSQFlA0hAQGHvnDZgyypm1yunObVWcWiF9KM7eGCJKYKLgTC2Fi/57OdnBhbj4OkzPGdFQJ6HyA==} + '@oxlint/binding-darwin-x64@1.81.0': + resolution: {integrity: sha512-q0QTm32jWga2Gv4j7IaVZN0jYMi9UV73sWVgFtDA4iIfqwMCLLZ3ve+9KwfYtsaKZSgQhmPaogeZWqDZpcY1Pw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.80.0': - resolution: {integrity: sha512-AYUIk5QnL0s8oWAYsREZwkRYy1SupJTXALo93J1TgzHywxQtdM99FecRMQ87MXEdPQ0j1TmEpeeq3fGNkpvMqg==} + '@oxlint/binding-freebsd-x64@1.81.0': + resolution: {integrity: sha512-/+8wVWDXEC7wHVAhOc59Fw/SkMc1arLkFD8iQCaSsmzenK1X4doFqquL9H1wrtGUzaiycVqkf/sSpcILK6W1UA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.80.0': - resolution: {integrity: sha512-9hBZVANupQ89W9dXyE0n8doCyaW5pDyGn3y6XlIMPZ+rIKuyqkr3SNUXmVJIhuvUq0NBU3RBiSXXE69l4XI6KA==} + '@oxlint/binding-linux-arm-gnueabihf@1.81.0': + resolution: {integrity: sha512-4xt422FEgioRq9hAL4Tq7fujGUWnc8z1BJ+Oi8RN8vB8axaP+sdK6a2xdlcQCCYnJg9QMuMFS0AucuIFx/EacA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.80.0': - resolution: {integrity: sha512-SvS2uKqzY+pbfuvAHzH4338R6Zwo805GAwrIMVvK1KxoOWCIjZUdfzTCvilD7z6JK91v011+zYMryabhDo2AsQ==} + '@oxlint/binding-linux-arm-musleabihf@1.81.0': + resolution: {integrity: sha512-u3vna8KdGplH4DRCW9K54D68fcMo7IxVrkCJWwXnIhwtBdnDnYrmzOUA/XjmBlPpcLsgw9Z5BNdY4za9+Dj+MQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.80.0': - resolution: {integrity: sha512-tCLadyqRVL3pQTRPNg7cjXKvcvS4fbyXeQHhKk5BTJ1oftQln5/yIIWbu/Xom/DX41zv2P9QGt6+D/TtQVtY3A==} + '@oxlint/binding-linux-arm64-gnu@1.81.0': + resolution: {integrity: sha512-3j9k+gsYsE7nv71GWotXsqsa2l9/aJenD7dVHNt/CBvsb0SgRjSMnHFeP59IXUAl1wvVFhqGl2wJNMwWU3UBlA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.80.0': - resolution: {integrity: sha512-XfpCNRlOPcLlJl4Bn/FUhjqlR6BVavEykERBf/MV7YA9VZDa5g5znVqYhyviMafcxS9Pe/i/kPvHNO0U6svEHQ==} + '@oxlint/binding-linux-arm64-musl@1.81.0': + resolution: {integrity: sha512-k5iAp3dNxW0/uDCBY+WSm8jKB2szu7SkEQZdgRRpDXvuDd69vvDcqhB3A/pWCfCwXyenjNjFn9Td1fVoyAc+Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.80.0': - resolution: {integrity: sha512-3I4yMwcFG9NeO8ioY6JBBuKsIm5GL/x7MATt1S4tVWaxPu5HcJ+XnLUbcVBTxG8q2Wu56HSj+NmXQiVYb1lp6A==} + '@oxlint/binding-linux-ppc64-gnu@1.81.0': + resolution: {integrity: sha512-TFqLja3uYmVSte6nof9GWrex9Z8WgdZrNiLC6Te5rXGDqXB2y4j/26iFhwosXiAFqDhE9JJVuuCkDKLwptTn1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.80.0': - resolution: {integrity: sha512-E1wAKymkpe1/E8helzBKdm81OBOF+ezxRyXRMEuik3ZpWDER5CPOKZwF66RsdwW98uwZv8UTFremUQtC1CzdJA==} + '@oxlint/binding-linux-riscv64-gnu@1.81.0': + resolution: {integrity: sha512-UEcySvGS0NOVo7h7n7CYyJL9+6gFAh7Zc/ToDXVScFvzHSTIxtzkMVU30rmQ6+nQ1LF+UdiRDdJajpDu+OylLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.80.0': - resolution: {integrity: sha512-+gLRGD4sIo3+VA++iham5UxD9tKSoJ/VOrROCEXIcknrYtQg6iIQgvjN0cpiRF7N6UYC7pJbvHJlDnMge5LRpQ==} + '@oxlint/binding-linux-riscv64-musl@1.81.0': + resolution: {integrity: sha512-H+diDbhD00+wI1IRP8Kz88x/lat+DgtoBJzoTthS16xkTJGNaEkfb8gzmd1rzc/2uDQQMl7GNl+JFUacVeWxIA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.80.0': - resolution: {integrity: sha512-aR0PrzHj9leW3NmzBAAP4EzdoBNoJcs9sjnIQPIwyRnBGYrRbXUIpEB5Q39AqK3PLY5JK5uEhDQDiUa1QSAstw==} + '@oxlint/binding-linux-s390x-gnu@1.81.0': + resolution: {integrity: sha512-8znJ/5TekjOKg1j1Acho4PJMdiAHLtlcXuWEiipOhAMV6rQcXdmDdXCbheyDczN6TjBwiNfjcP81k4AthrKRzw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.80.0': - resolution: {integrity: sha512-vSVh5cSo3Xxs6ghBCcFJlpbkbENzDog1qXtoXLa/HC3aCrR4XO76GZbXmQoCPHnu99nQpdCeC3H9tdNICfDh7A==} + '@oxlint/binding-linux-x64-gnu@1.81.0': + resolution: {integrity: sha512-Q2Wj70yFsvn5QjlmifFzbj4H+kJy53bwqc41o1fzoM7MpLV1NIbhg/LpWXRfC6KOkSAdUx1Wd8VJsdPmhp/HRA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.80.0': - resolution: {integrity: sha512-FfzBXpNQ8u7/ZI/p8bl73MeZ508Ax3hxWp3SiJpEFiC+BB9XcXy5FAZHTLKDPSzrUpxQZSZJAVdDmuJp/+HDBQ==} + '@oxlint/binding-linux-x64-musl@1.81.0': + resolution: {integrity: sha512-cPInHp/ddEe5qkyK2IiyQ8Q3Mp2oLLEhhsGgTK2oZx4L6+llGam1H1yBvJZ7qHfOXj8N3hxBS8sj4tO+gtFlIg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.80.0': - resolution: {integrity: sha512-zMzbkumtmprCgRwoYNzcB3iC39fXdJIMLMU33KdCjEGLlJGOEt1+LwQ4LF8ndLzAEKVz4BR0y3V6Xrkk3Nm3yA==} + '@oxlint/binding-openharmony-arm64@1.81.0': + resolution: {integrity: sha512-0CQxSX4ajqm07AHBf5U33qQzXKdd7wtq/oTL/7vpY6RNNuxrRi8W4bqUV1Jyu/vj+9KmxQyDhxfeVX1nQL6kfg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.80.0': - resolution: {integrity: sha512-ib6iRcrXsk4t1fm3iKcwksyWh1ZkZXC/2mEzakl0ai2+6HZunf1WWMZ/xP9EJAvw9g9K4UVTC3NF/+G2qLrbTQ==} + '@oxlint/binding-win32-arm64-msvc@1.81.0': + resolution: {integrity: sha512-l0hbeISm9673hVrrQU8j/p2M7YH9Ouoj7p7E/QM55NTrKVLP+P3PF8hLu+OY+x0VtGRW+ggiQKZqmdYps9H+TA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.80.0': - resolution: {integrity: sha512-xhRWBMpLxZvgKAH6+DJZmpP+W8Y8UdQOSU1JfxSWNXsaBaRGW77j+1hCuNHlzj7OH4SPN8fYd1q0o2qrDtoVyw==} + '@oxlint/binding-win32-ia32-msvc@1.81.0': + resolution: {integrity: sha512-ksqPP5jbFXcYreEQ7zdJh06rJQBymCTyGRCdaXjfcf2aG4f8KxUWY5wcgYHmaTK+FJ4bPG5sUAdOX+6trnH1JA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.80.0': - resolution: {integrity: sha512-yAnO7lwBYQnz2pcfBPIGQQZWIX5zd5R/1aAKIF3oE+TVj7IhoHcROjOkz3sRDngzqhfPKfFaXqug5j5rE5dn6Q==} + '@oxlint/binding-win32-x64-msvc@1.81.0': + resolution: {integrity: sha512-IZuUCwGw9emG5JtCp+fYGB+Z4OWEoeEcM8R5BA1pYw63/ieYFVdcU2ylxTpHbVHSenZnsYE+ZZ20uHAJszQ4cA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -955,8 +955,8 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - '@types/node@26.4.0': - resolution: {integrity: sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==} + '@types/node@26.4.1': + resolution: {integrity: sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==} '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} @@ -1227,9 +1227,9 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} - content-type@2.1.0: - resolution: {integrity: sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==} - engines: {node: '>=18'} + content-type@3.0.0: + resolution: {integrity: sha512-AIi5H6p0xk5uknXcN3/rmhP8jgp69OfSe/JuKiQAFprJ7UGw7mwj7m4XcmDzlrnJDG+cGpphAINGdU3g3g7kDw==} + engines: {node: '>=22'} convert-to-spaces@2.0.1: resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} @@ -1271,8 +1271,8 @@ packages: emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - es-toolkit@1.51.0: - resolution: {integrity: sha512-zC2lQGkM7QX+Gm6iM3+WIdZJzthsEd14LvRNJneSO2hzyz/zNBENR8+YXWo1cKxgPBtV6ksPYHELbcwBRzmdCw==} + es-toolkit@1.52.0: + resolution: {integrity: sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==} esbuild@0.28.2: resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} @@ -1317,8 +1317,8 @@ packages: fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fastq@1.20.3: + resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} @@ -1356,8 +1356,8 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} - globby@16.2.3: - resolution: {integrity: sha512-VZX7TV7jmd/pn71vdnLKtgwy1IWqc3KjI9x1/UtPkwoKk5fKrNLY30ltDe3cAM5xruIN7YuuaulFt133jRrKZg==} + globby@16.2.4: + resolution: {integrity: sha512-c8B/VNLmxRcmqqenRA9t+9IyOjf9+V6lTxPaUJLqOCONdQkWZ0ETYgX0qbtJqPsgCNusT9MZ5Jeidw8Eb9tn2g==} engines: {node: '>=20'} graceful-fs@4.2.11: @@ -1375,8 +1375,8 @@ packages: resolution: {integrity: sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==} engines: {node: '>=10 <11 || >=12 <13 || >=14'} - ignore@7.0.6: - resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + ignore@7.0.8: + resolution: {integrity: sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==} engines: {node: '>= 4'} indent-string@5.0.0: @@ -1407,8 +1407,8 @@ packages: resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} engines: {node: '>=12'} - is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + is-plain-object@5.1.0: + resolution: {integrity: sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw==} engines: {node: '>=0.10.0'} is-promise@4.0.0: @@ -1425,15 +1425,15 @@ packages: js-tiktoken@1.0.21: resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==} - js-yaml@4.3.1: - resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true json-with-bigint@3.5.12: resolution: {integrity: sha512-uwbF/wSSuOgC7qqlq27Xp5B6a2MHVug3t0idZdTqu0JnlFvgJuH7ju+KAk/J06C7GfhoYy2gnb9wz2INqcne7w==} - langsmith@0.9.0: - resolution: {integrity: sha512-tlg/aG7qezAKY6G3fgADSX7PkRj+JKoF3z7QNkCMsAOvwvuzhiwP9Amn1Z+zAIxuKoWuXQdIjtFN0LVmUC1oUQ==} + langsmith@0.10.1: + resolution: {integrity: sha512-zRDCnLznGdzx1VottX4CWr8v9ZZLRoSql2pbjEXYA1Jeg+NMDdq87x/v0Dk4GNkNZYhE+ZxFX3vTxwWq6W6gVA==} peerDependencies: '@opentelemetry/api': '*' '@opentelemetry/exporter-trace-otlp-proto': '*' @@ -1532,8 +1532,8 @@ packages: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} - oxlint@1.80.0: - resolution: {integrity: sha512-5nTiSps4qdbCWLbxzuO00alHkEO2exR9YMN/ig6QXWrLsYSG0KaObOAM+l6oU2LcKPWoSAGYbkZIGEu1ViiWKA==} + oxlint@1.81.0: + resolution: {integrity: sha512-HyrJYqeoOCL0iqaLEzGewGT48ZX99P3hxYh8udAF9RGGIghSamkXE4ClUyBpEDNqasamThgmlPbuMOe7SAZmHg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -1549,8 +1549,8 @@ packages: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} - p-map@7.0.6: - resolution: {integrity: sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg==} + p-map@7.0.7: + resolution: {integrity: sha512-VaWRu2i4FJNRtiRWCuuQRgfQ1B7a6+gMSrO+3j0EQi/k0ULfS9kosRxGoiqwzIjZTDI02tGfk5mXXltLg6QtfQ==} engines: {node: '>=18'} p-queue@6.6.2: @@ -1577,8 +1577,8 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.5: - resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} plur@6.0.0: @@ -1590,8 +1590,8 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-ms@9.3.0: - resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + pretty-ms@9.3.1: + resolution: {integrity: sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==} engines: {node: '>=18'} queue-microtask@1.2.3: @@ -1678,8 +1678,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.23.12: - resolution: {integrity: sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==} + tsx@4.23.13: + resolution: {integrity: sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==} engines: {node: '>=18.0.0'} hasBin: true @@ -1744,8 +1744,8 @@ packages: resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zod@4.5.4: + resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} snapshots: @@ -1865,122 +1865,122 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@5.2.1(@types/node@26.4.0)': + '@inquirer/checkbox@5.2.3(@types/node@26.4.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/figures': 2.0.8 + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/confirm@6.1.1(@types/node@26.4.0)': + '@inquirer/confirm@6.3.0(@types/node@26.4.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/core@11.2.1(@types/node@26.4.0)': + '@inquirer/core@12.0.1(@types/node@26.4.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/figures': 2.0.8 + '@inquirer/type': 4.1.0(@types/node@26.4.1) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/editor@5.2.2(@types/node@26.4.0)': + '@inquirer/editor@5.3.1(@types/node@26.4.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/external-editor': 3.0.3(@types/node@26.4.0) - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/external-editor': 3.0.4(@types/node@26.4.1) + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/expand@5.1.1(@types/node@26.4.0)': + '@inquirer/expand@5.1.3(@types/node@26.4.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/external-editor@3.0.3(@types/node@26.4.0)': + '@inquirer/external-editor@3.0.4(@types/node@26.4.1)': dependencies: chardet: 2.2.0 iconv-lite: 0.7.3 optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/figures@2.0.7': {} + '@inquirer/figures@2.0.8': {} - '@inquirer/input@5.1.2(@types/node@26.4.0)': + '@inquirer/input@5.1.4(@types/node@26.4.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/number@4.1.1(@types/node@26.4.0)': + '@inquirer/number@4.2.1(@types/node@26.4.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/password@5.1.1(@types/node@26.4.0)': + '@inquirer/password@5.2.0(@types/node@26.4.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 - - '@inquirer/prompts@8.5.2(@types/node@26.4.0)': - dependencies: - '@inquirer/checkbox': 5.2.1(@types/node@26.4.0) - '@inquirer/confirm': 6.1.1(@types/node@26.4.0) - '@inquirer/editor': 5.2.2(@types/node@26.4.0) - '@inquirer/expand': 5.1.1(@types/node@26.4.0) - '@inquirer/input': 5.1.2(@types/node@26.4.0) - '@inquirer/number': 4.1.1(@types/node@26.4.0) - '@inquirer/password': 5.1.1(@types/node@26.4.0) - '@inquirer/rawlist': 5.3.1(@types/node@26.4.0) - '@inquirer/search': 4.2.1(@types/node@26.4.0) - '@inquirer/select': 5.2.1(@types/node@26.4.0) + '@types/node': 26.4.1 + + '@inquirer/prompts@8.7.0(@types/node@26.4.1)': + dependencies: + '@inquirer/checkbox': 5.2.3(@types/node@26.4.1) + '@inquirer/confirm': 6.3.0(@types/node@26.4.1) + '@inquirer/editor': 5.3.1(@types/node@26.4.1) + '@inquirer/expand': 5.1.3(@types/node@26.4.1) + '@inquirer/input': 5.1.4(@types/node@26.4.1) + '@inquirer/number': 4.2.1(@types/node@26.4.1) + '@inquirer/password': 5.2.0(@types/node@26.4.1) + '@inquirer/rawlist': 5.3.3(@types/node@26.4.1) + '@inquirer/search': 4.3.1(@types/node@26.4.1) + '@inquirer/select': 5.2.3(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/rawlist@5.3.1(@types/node@26.4.0)': + '@inquirer/rawlist@5.3.3(@types/node@26.4.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/search@4.2.1(@types/node@26.4.0)': + '@inquirer/search@4.3.1(@types/node@26.4.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/figures': 2.0.8 + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/select@5.2.1(@types/node@26.4.0)': + '@inquirer/select@5.2.3(@types/node@26.4.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 12.0.1(@types/node@26.4.1) + '@inquirer/figures': 2.0.8 + '@inquirer/type': 4.1.0(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/type@4.0.7(@types/node@26.4.0)': + '@inquirer/type@4.1.0(@types/node@26.4.1)': optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@isaacs/fs-minipass@4.0.1': dependencies: @@ -1991,10 +1991,10 @@ snapshots: '@cfworker/json-schema': 4.1.1 '@standard-schema/spec': 1.1.0 js-tiktoken: 1.0.21 - langsmith: 0.9.0 + langsmith: 0.10.1 mustache: 4.2.0 p-queue: 6.6.2 - zod: 4.4.3 + zod: 4.5.4 transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/exporter-trace-otlp-proto' @@ -2015,16 +2015,16 @@ snapshots: - encoding - supports-color - '@napi-rs/cli@3.8.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@26.4.0)(emnapi@1.10.0)': + '@napi-rs/cli@3.9.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@26.4.1)(emnapi@1.10.0)': dependencies: - '@inquirer/prompts': 8.5.2(@types/node@26.4.0) + '@inquirer/prompts': 8.7.0(@types/node@26.4.1) '@napi-rs/cross-toolchain': 1.0.3 '@napi-rs/wasm-tools': 1.1.0 '@octokit/rest': 22.0.1 clipanion: 4.0.0-rc.4(typanion@3.14.0) colorette: 2.0.20 - es-toolkit: 1.51.0 - js-yaml: 4.3.1 + es-toolkit: 1.52.0 + js-yaml: 4.3.2 obug: 2.1.4 semver: 7.8.5 typanion: 3.14.0 @@ -2284,139 +2284,139 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 + fastq: 1.20.3 '@octokit/auth-token@6.0.0': {} - '@octokit/core@7.0.7': + '@octokit/core@7.0.8': dependencies: '@octokit/auth-token': 6.0.0 - '@octokit/graphql': 9.0.4 - '@octokit/request': 10.0.14 - '@octokit/request-error': 7.1.1 - '@octokit/types': 17.0.0 + '@octokit/graphql': 9.0.5 + '@octokit/request': 10.0.16 + '@octokit/request-error': 7.1.2 + '@octokit/types': 18.0.0 before-after-hook: 4.0.0 universal-user-agent: 7.0.3 - '@octokit/endpoint@11.0.4': + '@octokit/endpoint@11.0.5': dependencies: - '@octokit/types': 17.0.0 + '@octokit/types': 18.0.0 universal-user-agent: 7.0.3 - '@octokit/graphql@9.0.4': + '@octokit/graphql@9.0.5': dependencies: - '@octokit/request': 10.0.14 - '@octokit/types': 17.0.0 + '@octokit/request': 10.0.16 + '@octokit/types': 18.0.0 universal-user-agent: 7.0.3 '@octokit/openapi-types@27.0.0': {} - '@octokit/openapi-types@28.0.0': {} + '@octokit/openapi-types@29.0.1': {} - '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.7)': + '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.8)': dependencies: - '@octokit/core': 7.0.7 + '@octokit/core': 7.0.8 '@octokit/types': 16.0.0 - '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.7)': + '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.8)': dependencies: - '@octokit/core': 7.0.7 + '@octokit/core': 7.0.8 - '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.7)': + '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.8)': dependencies: - '@octokit/core': 7.0.7 + '@octokit/core': 7.0.8 '@octokit/types': 16.0.0 - '@octokit/request-error@7.1.1': + '@octokit/request-error@7.1.2': dependencies: - '@octokit/types': 17.0.0 + '@octokit/types': 18.0.0 - '@octokit/request@10.0.14': + '@octokit/request@10.0.16': dependencies: - '@octokit/endpoint': 11.0.4 - '@octokit/request-error': 7.1.1 - '@octokit/types': 17.0.0 - content-type: 2.1.0 + '@octokit/endpoint': 11.0.5 + '@octokit/request-error': 7.1.2 + '@octokit/types': 18.0.0 + content-type: 3.0.0 json-with-bigint: 3.5.12 universal-user-agent: 7.0.3 '@octokit/rest@22.0.1': dependencies: - '@octokit/core': 7.0.7 - '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.7) - '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.7) - '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.7) + '@octokit/core': 7.0.8 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.8) + '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.8) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.8) '@octokit/types@16.0.0': dependencies: '@octokit/openapi-types': 27.0.0 - '@octokit/types@17.0.0': + '@octokit/types@18.0.0': dependencies: - '@octokit/openapi-types': 28.0.0 + '@octokit/openapi-types': 29.0.1 - '@oxlint/binding-android-arm-eabi@1.80.0': + '@oxlint/binding-android-arm-eabi@1.81.0': optional: true - '@oxlint/binding-android-arm64@1.80.0': + '@oxlint/binding-android-arm64@1.81.0': optional: true - '@oxlint/binding-darwin-arm64@1.80.0': + '@oxlint/binding-darwin-arm64@1.81.0': optional: true - '@oxlint/binding-darwin-x64@1.80.0': + '@oxlint/binding-darwin-x64@1.81.0': optional: true - '@oxlint/binding-freebsd-x64@1.80.0': + '@oxlint/binding-freebsd-x64@1.81.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.80.0': + '@oxlint/binding-linux-arm-gnueabihf@1.81.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.80.0': + '@oxlint/binding-linux-arm-musleabihf@1.81.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.80.0': + '@oxlint/binding-linux-arm64-gnu@1.81.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.80.0': + '@oxlint/binding-linux-arm64-musl@1.81.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.80.0': + '@oxlint/binding-linux-ppc64-gnu@1.81.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.80.0': + '@oxlint/binding-linux-riscv64-gnu@1.81.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.80.0': + '@oxlint/binding-linux-riscv64-musl@1.81.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.80.0': + '@oxlint/binding-linux-s390x-gnu@1.81.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.80.0': + '@oxlint/binding-linux-x64-gnu@1.81.0': optional: true - '@oxlint/binding-linux-x64-musl@1.80.0': + '@oxlint/binding-linux-x64-musl@1.81.0': optional: true - '@oxlint/binding-openharmony-arm64@1.80.0': + '@oxlint/binding-openharmony-arm64@1.81.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.80.0': + '@oxlint/binding-win32-arm64-msvc@1.81.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.80.0': + '@oxlint/binding-win32-ia32-msvc@1.81.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.80.0': + '@oxlint/binding-win32-x64-msvc@1.81.0': optional: true '@rollup/pluginutils@5.4.0': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.5 + picomatch: 4.0.7 '@sindresorhus/merge-streams@4.0.0': {} @@ -2429,7 +2429,7 @@ snapshots: '@types/estree@1.0.9': {} - '@types/node@26.4.0': + '@types/node@26.4.1': dependencies: undici-types: 8.3.0 @@ -2505,7 +2505,7 @@ snapshots: glob: 13.0.6 graceful-fs: 4.2.11 node-gyp-build: 4.8.4 - picomatch: 4.0.5 + picomatch: 4.0.7 resolve-from: 5.0.0 transitivePeerDependencies: - encoding @@ -2562,19 +2562,19 @@ snapshots: debug: 4.4.3 emittery: 2.0.0 figures: 6.1.0 - globby: 16.2.3 + globby: 16.2.4 ignore-by-default: 2.1.0 indent-string: 5.0.0 - is-plain-object: 5.0.0 + is-plain-object: 5.1.0 is-promise: 4.0.0 matcher: 6.0.0 memoize: 11.0.0 ms: 2.1.3 - p-map: 7.0.6 + p-map: 7.0.7 package-config: 5.0.0 - picomatch: 4.0.5 + picomatch: 4.0.7 plur: 6.0.0 - pretty-ms: 9.3.0 + pretty-ms: 9.3.1 resolve-cwd: 3.0.0 slash: 5.1.0 stack-utils: 2.0.6 @@ -2663,7 +2663,7 @@ snapshots: consola@3.4.2: {} - content-type@2.1.0: {} + content-type@3.0.0: {} convert-to-spaces@2.0.1: {} @@ -2688,7 +2688,7 @@ snapshots: emoji-regex@10.6.0: {} - es-toolkit@1.51.0: {} + es-toolkit@1.52.0: {} esbuild@0.28.2: optionalDependencies: @@ -2751,7 +2751,7 @@ snapshots: dependencies: fast-string-width: 3.0.2 - fastq@1.20.1: + fastq@1.20.3: dependencies: reusify: 1.1.0 @@ -2784,12 +2784,13 @@ snapshots: minipass: 7.1.3 path-scurry: 2.0.2 - globby@16.2.3: + globby@16.2.4: dependencies: '@sindresorhus/merge-streams': 4.0.0 fast-glob: 3.3.3 - ignore: 7.0.6 + ignore: 7.0.8 is-path-inside: 4.0.0 + micromatch: 4.0.8 slash: 5.1.0 unicorn-magic: 0.4.0 @@ -2808,7 +2809,7 @@ snapshots: ignore-by-default@2.1.0: {} - ignore@7.0.6: {} + ignore@7.0.8: {} indent-string@5.0.0: {} @@ -2828,7 +2829,7 @@ snapshots: is-path-inside@4.0.0: {} - is-plain-object@5.0.0: {} + is-plain-object@5.1.0: {} is-promise@4.0.0: {} @@ -2840,13 +2841,13 @@ snapshots: dependencies: base64-js: 1.5.1 - js-yaml@4.3.1: + js-yaml@4.3.2: dependencies: argparse: 2.0.1 json-with-bigint@3.5.12: {} - langsmith@0.9.0: + langsmith@0.10.1: dependencies: p-queue: 6.6.2 @@ -2905,31 +2906,31 @@ snapshots: obug@2.1.4: {} - oxlint@1.80.0: + oxlint@1.81.0: optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.80.0 - '@oxlint/binding-android-arm64': 1.80.0 - '@oxlint/binding-darwin-arm64': 1.80.0 - '@oxlint/binding-darwin-x64': 1.80.0 - '@oxlint/binding-freebsd-x64': 1.80.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.80.0 - '@oxlint/binding-linux-arm-musleabihf': 1.80.0 - '@oxlint/binding-linux-arm64-gnu': 1.80.0 - '@oxlint/binding-linux-arm64-musl': 1.80.0 - '@oxlint/binding-linux-ppc64-gnu': 1.80.0 - '@oxlint/binding-linux-riscv64-gnu': 1.80.0 - '@oxlint/binding-linux-riscv64-musl': 1.80.0 - '@oxlint/binding-linux-s390x-gnu': 1.80.0 - '@oxlint/binding-linux-x64-gnu': 1.80.0 - '@oxlint/binding-linux-x64-musl': 1.80.0 - '@oxlint/binding-openharmony-arm64': 1.80.0 - '@oxlint/binding-win32-arm64-msvc': 1.80.0 - '@oxlint/binding-win32-ia32-msvc': 1.80.0 - '@oxlint/binding-win32-x64-msvc': 1.80.0 + '@oxlint/binding-android-arm-eabi': 1.81.0 + '@oxlint/binding-android-arm64': 1.81.0 + '@oxlint/binding-darwin-arm64': 1.81.0 + '@oxlint/binding-darwin-x64': 1.81.0 + '@oxlint/binding-freebsd-x64': 1.81.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.81.0 + '@oxlint/binding-linux-arm-musleabihf': 1.81.0 + '@oxlint/binding-linux-arm64-gnu': 1.81.0 + '@oxlint/binding-linux-arm64-musl': 1.81.0 + '@oxlint/binding-linux-ppc64-gnu': 1.81.0 + '@oxlint/binding-linux-riscv64-gnu': 1.81.0 + '@oxlint/binding-linux-riscv64-musl': 1.81.0 + '@oxlint/binding-linux-s390x-gnu': 1.81.0 + '@oxlint/binding-linux-x64-gnu': 1.81.0 + '@oxlint/binding-linux-x64-musl': 1.81.0 + '@oxlint/binding-openharmony-arm64': 1.81.0 + '@oxlint/binding-win32-arm64-msvc': 1.81.0 + '@oxlint/binding-win32-ia32-msvc': 1.81.0 + '@oxlint/binding-win32-x64-msvc': 1.81.0 p-finally@1.0.0: {} - p-map@7.0.6: {} + p-map@7.0.7: {} p-queue@6.6.2: dependencies: @@ -2954,7 +2955,7 @@ snapshots: picomatch@2.3.2: {} - picomatch@4.0.5: {} + picomatch@4.0.7: {} plur@6.0.0: dependencies: @@ -2962,7 +2963,7 @@ snapshots: prettier@3.9.6: {} - pretty-ms@9.3.0: + pretty-ms@9.3.1: dependencies: parse-ms: 4.0.0 @@ -3019,7 +3020,7 @@ snapshots: supertap@3.0.1(patch_hash=fb62acff1ae3d1587268fef569b79684229eeb16c6a833a14b115a253cc00112): dependencies: indent-string: 5.0.0 - js-yaml: 4.3.1 + js-yaml: 4.3.2 serialize-error: 7.0.1 strip-ansi: 7.2.0 @@ -3044,7 +3045,7 @@ snapshots: tslib@2.8.1: optional: true - tsx@4.23.12: + tsx@4.23.13: dependencies: esbuild: 0.28.2 optionalDependencies: @@ -3119,4 +3120,4 @@ snapshots: y18n: 5.0.8 yargs-parser: 22.0.0 - zod@4.4.3: {} + zod@4.5.4: {} diff --git a/crates/bashkit-js/wrapper.ts b/crates/bashkit-js/wrapper.ts index 194adb494..b6bf15e79 100644 --- a/crates/bashkit-js/wrapper.ts +++ b/crates/bashkit-js/wrapper.ts @@ -1666,6 +1666,7 @@ export class BashTool { toNativeOptions(options, resolved), toNativeSnapshotOptions(snapshotOptions), ); + registerCustomBuiltins(instance.native, options?.customBuiltins); instance.maxInputBytes = options?.maxInputBytes ?? DEFAULT_MAX_INPUT_BYTES; return instance; } @@ -1685,6 +1686,7 @@ export class BashTool { Buffer.from(key), toNativeOptions(options, resolved), ); + registerCustomBuiltins(instance.native, options?.customBuiltins); instance.maxInputBytes = options?.maxInputBytes ?? DEFAULT_MAX_INPUT_BYTES; return instance; } diff --git a/crates/bashkit-python/README.md b/crates/bashkit-python/README.md index 81dd3a3c4..52792e3b1 100644 --- a/crates/bashkit-python/README.md +++ b/crates/bashkit-python/README.md @@ -562,10 +562,22 @@ tool = create_bash_tool() ### Deep Agents +Requires Python 3.11+ and `pip install 'bashkit[deepagents]'`. + ```python -from bashkit.deepagents import BashkitBackend, BashkitMiddleware +from bashkit.deepagents import BashkitBackend + +backend = BashkitBackend(timeout_seconds=30) +backend.write("/tmp/example.py", "print(42)") +assert backend.read("/tmp/example.py").file_data["content"] == "print(42)" +assert backend.grep("print", "/tmp", glob="*.py").matches[0]["line"] == 1 +middleware = backend.create_middleware() # Shares the same virtual filesystem ``` +Uses the structured Deep Agents 0.7.13+ backend protocol. Shell execution reports +native output truncation; file operations preserve exact VFS contents. Configure +execution timeouts on the backend constructor. + ## API Reference ### Bash diff --git a/crates/bashkit-python/bashkit/_bashkit.pyi b/crates/bashkit-python/bashkit/_bashkit.pyi index c9a7fa797..824f75230 100644 --- a/crates/bashkit-python/bashkit/_bashkit.pyi +++ b/crates/bashkit-python/bashkit/_bashkit.pyi @@ -1046,6 +1046,8 @@ class ExecResult: stderr: str exit_code: int error: str | None + stdout_truncated: bool + stderr_truncated: bool success: bool def to_dict(self) -> dict[str, Any]: diff --git a/crates/bashkit-python/bashkit/deepagents.py b/crates/bashkit-python/bashkit/deepagents.py index 8093089ed..4f89a2052 100644 --- a/crates/bashkit-python/bashkit/deepagents.py +++ b/crates/bashkit-python/bashkit/deepagents.py @@ -4,7 +4,7 @@ Provides middleware and backend for Deep Agents using Bashkit's VFS: - ``BashkitMiddleware``: Adds ``bash`` tool via ``AgentMiddleware.tools`` -- ``BashkitBackend``: ``SandboxBackendProtocol`` for execute/read_file/write_file/etc. +- ``BashkitBackend``: ``SandboxBackendProtocol`` for execute/read/write/ls/glob/grep. Standalone middleware (creates its own VFS):: @@ -28,52 +28,39 @@ from __future__ import annotations -import secrets -import shlex +import posixpath import uuid -from datetime import datetime, timezone from typing import TYPE_CHECKING from bashkit import BashTool as NativeBashTool -if TYPE_CHECKING: - pass - # Check for deepagents availability try: from deepagents.backends.protocol import ( + DeleteResult, EditResult, ExecuteResponse, FileDownloadResponse, FileInfo, FileUploadResponse, + GlobResult, GrepMatch, + GrepResult, + LsResult, + ReadResult, SandboxBackendProtocol, WriteResult, ) + from deepagents.backends.utils import compile_grep_include_glob from langchain.agents.middleware.types import AgentMiddleware from langchain_core.tools import tool as langchain_tool DEEPAGENTS_AVAILABLE = True except ImportError: DEEPAGENTS_AVAILABLE = False - SandboxBackendProtocol = object - AgentMiddleware = object - - -def _now_iso() -> str: - return datetime.now(timezone.utc).isoformat() - - -def _build_write_cmd(file_path: str, content: str) -> str: - """Build a heredoc command with a randomized delimiter to prevent injection. - - A fixed delimiter like BASHKIT_EOF can be terminated early by content - containing that literal string on its own line. Using a random suffix - makes it infeasible for content to match the delimiter. - """ - delimiter = f"BASHKIT_EOF_{secrets.token_hex(8)}" - return f"cat > {shlex.quote(file_path)} << '{delimiter}'\n{content}\n{delimiter}" + if not TYPE_CHECKING: + SandboxBackendProtocol = object + AgentMiddleware = object def _make_bash_tool(bash_instance: NativeBashTool, max_output_length: int = 100_000): @@ -170,7 +157,7 @@ def reset(self) -> None: class BashkitBackend(SandboxBackendProtocol): """Backend implementing SandboxBackendProtocol with Bashkit VFS. - Provides execute, read_file, write_file, edit_file, ls, glob, grep + Provides execute, read, write, edit, delete, ls, glob, grep all operating on the same virtual filesystem. Example: @@ -212,163 +199,167 @@ def create_middleware(self) -> BashkitMiddleware: """ return BashkitMiddleware(bash_tool=self._bash) - # === Shell Execution === + # File helpers use the live VFS directly: shell output caps and delimiter + # parsing must never silently corrupt file contents or discovered paths. + # Async protocol defaults dispatch these sync methods off the event loop. - def execute(self, command: str) -> ExecuteResponse: + # Deep Agents explicitly detects backends without per-call timeout support. + # Keep the keyword absent so the framework uses our constructor timeout. + def execute(self, command: str) -> ExecuteResponse: # type: ignore[override] result = self._bash.execute_sync(command) output = result.stdout + (result.stderr or "") if result.error and result.error not in output: output += f"\nError: {result.error}" - return ExecuteResponse(output=output, exit_code=result.exit_code, truncated=False) - - async def aexecute(self, command: str) -> ExecuteResponse: - return self.execute(command) - - # === File Operations === + return ExecuteResponse( + output=output, + exit_code=result.exit_code, + truncated=result.stdout_truncated or result.stderr_truncated, + ) - def read(self, file_path: str, offset: int = 0, limit: int = 2000) -> str: - result = self._bash.execute_sync(f"cat {shlex.quote(file_path)}") - if result.exit_code != 0: - return f"Error: {result.stderr or 'File not found'}" - lines = result.stdout.splitlines() + def _path(self, path: str) -> str: + return posixpath.normpath(posixpath.join(self._bash.shell_state().cwd, path)) + + def read(self, file_path: str, offset: int = 0, limit: int = 2000) -> ReadResult: + if limit <= 0: + return ReadResult(file_data={"content": "", "encoding": "utf-8"}, no_lines_requested=True) + try: + lines = self._bash.read_file(self._path(file_path)).splitlines() + except (RuntimeError, ValueError) as exc: + return ReadResult(error=str(exc)) + offset = max(0, offset) selected = lines[offset : offset + limit] - return "\n".join(f"{i:6d}\t{line}" for i, line in enumerate(selected, start=offset + 1)) - - async def aread(self, file_path: str, offset: int = 0, limit: int = 2000) -> str: - return self.read(file_path, offset, limit) + start_line = end_line = total_lines = next_offset = None + if selected: + end = offset + len(selected) + start_line, end_line, total_lines = offset + 1, end, len(lines) + if end < len(lines): + next_offset = end + return ReadResult( + file_data={"content": "\n".join(selected), "encoding": "utf-8"}, + start_line=start_line, + end_line=end_line, + total_lines=total_lines, + next_offset=next_offset, + ) def write(self, file_path: str, content: str) -> WriteResult: - cmd = _build_write_cmd(file_path, content) - result = self._bash.execute_sync(cmd) - return WriteResult(error=result.stderr if result.exit_code != 0 else None, path=file_path) - - async def awrite(self, file_path: str, content: str) -> WriteResult: - return self.write(file_path, content) + path = self._path(file_path) + try: + if self._bash.exists(path): + return WriteResult(error=f"File already exists: {path}") + self._bash.mkdir(posixpath.dirname(path), recursive=True) + self._bash.write_file(path, content) + except (RuntimeError, ValueError) as exc: + return WriteResult(error=str(exc)) + return WriteResult(path=path) def edit(self, file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult: - result = self._bash.execute_sync(f"cat {shlex.quote(file_path)}") - if result.exit_code != 0: - return EditResult(error=f"File not found: {file_path}") - content = result.stdout - count = content.count(old_string) - if count == 0: - return EditResult(error="old_string not found") - if count > 1 and not replace_all: - return EditResult(error=f"Found {count} times. Use replace_all=True") - if replace_all: - new_content = content.replace(old_string, new_string) - else: - new_content = content.replace(old_string, new_string, 1) - wr = self.write(file_path, new_content) - return EditResult(error=wr.error, path=file_path) - - async def aedit( - self, file_path: str, old_string: str, new_string: str, replace_all: bool = False - ) -> EditResult: - return self.edit(file_path, old_string, new_string, replace_all) - - # === File Discovery === - - def ls_info(self, path: str) -> list[FileInfo]: - result = self._bash.execute_sync(f"ls -la {shlex.quote(path)}") - if result.exit_code != 0: - return [] - files = [] - for line in result.stdout.splitlines(): - parts = line.split() - if len(parts) < 9 or parts[0].startswith("total"): - continue - name = " ".join(parts[8:]) - if name in (".", ".."): - continue - files.append( + path = self._path(file_path) + try: + content = self._bash.read_file(path) + count = content.count(old_string) + if count == 0: + return EditResult(error="old_string not found") + if count > 1 and not replace_all: + return EditResult(error=f"Found {count} times. Use replace_all=True") + self._bash.write_file(path, content.replace(old_string, new_string, -1 if replace_all else 1)) + except (RuntimeError, ValueError) as exc: + return EditResult(error=str(exc)) + return EditResult(path=path, occurrences=count if replace_all else 1) + + def delete(self, file_path: str) -> DeleteResult: + path = self._path(file_path) + try: + if self._bash.stat(path)["file_type"] == "directory": + return DeleteResult(error="Cannot delete a directory") + self._bash.remove(path) + except (RuntimeError, ValueError) as exc: + return DeleteResult(error=str(exc)) + return DeleteResult(path=path) + + def ls(self, path: str) -> LsResult: + path = self._path(path) + try: + entries = [ FileInfo( - path=f"{path.rstrip('/')}/{name}", - name=name, - is_dir=parts[0].startswith("d"), - size=int(parts[4]) if parts[4].isdigit() else 0, - created_at=_now_iso(), - modified_at=_now_iso(), + path=posixpath.join(path, entry["name"]), + is_dir=entry["metadata"]["file_type"] == "directory", + size=entry["metadata"]["size"], ) - ) - return files - - async def als_info(self, path: str) -> list[FileInfo]: - return self.ls_info(path) - - def glob_info(self, pattern: str, path: str = "/") -> list[FileInfo]: - name_pattern = pattern.replace("**/", "").replace("**", "*") if "**" in pattern else pattern - result = self._bash.execute_sync(f"find {shlex.quote(path)} -name {shlex.quote(name_pattern)} -type f") - if result.exit_code != 0: - return [] - return [ - FileInfo( - path=p.strip(), - name=p.strip().split("/")[-1], - is_dir=False, - size=0, - created_at=_now_iso(), - modified_at=_now_iso(), - ) - for p in result.stdout.splitlines() - if p.strip() - ] - - async def aglob_info(self, pattern: str, path: str = "/") -> list[FileInfo]: - return self.glob_info(pattern, path) - - def grep_raw(self, pattern: str, path: str | None = None, glob: str | None = None) -> list[GrepMatch] | str: - quoted_pattern = shlex.quote(pattern) - search_path = shlex.quote(path) if path else "/home" - cmd = f"grep -rn {quoted_pattern} {search_path}" - result = self._bash.execute_sync(cmd) - matches = [] - for line in result.stdout.splitlines(): - if ":" not in line: - continue - parts = line.split(":", 2) - if len(parts) >= 3: - try: - matches.append(GrepMatch(path=parts[0], line_number=int(parts[1]), content=parts[2])) - except ValueError: + for entry in self._bash.read_dir(path) + ] + except (RuntimeError, ValueError) as exc: + return LsResult(error=str(exc)) + return LsResult(entries=sorted(entries, key=lambda entry: entry["path"])) + + def _files(self, root: str): + pending = [root] + while pending: + path = pending.pop() + metadata = self._bash.stat(path) + if metadata["file_type"] == "directory": + # Do not traverse symlink directories: cycles must not create + # an unbounded host-side walk outside shell execution limits. + entries = self._bash.read_dir(path) + pending.extend( + posixpath.join(path, entry["name"]) + for entry in reversed(sorted(entries, key=lambda entry: entry["name"])) + if entry["metadata"]["file_type"] in ("file", "directory") + ) + elif metadata["file_type"] == "file": + yield FileInfo(path=path, is_dir=False, size=metadata["size"]) + + def glob(self, pattern: str, path: str | None = None) -> GlobResult: + root = self._path(path or ".") + try: + matches = compile_grep_include_glob(pattern) + files = [info for info in self._files(root) if matches(posixpath.relpath(info["path"], root))] + except (RuntimeError, ValueError) as exc: + return GlobResult(error=str(exc)) + return GlobResult(matches=files) + + def grep( + self, pattern: str, path: str | None = None, glob: str | None = None, *, max_count: int | None = None + ) -> GrepResult: + root = self._path(path or ".") + matches: list[GrepMatch] = [] + try: + include = compile_grep_include_glob(glob) if glob is not None else lambda _: True + for info in self._files(root): + relative = posixpath.relpath(info["path"], root) + if not include(posixpath.basename(root) if relative == "." else relative): continue - return matches - - async def agrep_raw( - self, pattern: str, path: str | None = None, glob: str | None = None - ) -> list[GrepMatch] | str: - return self.grep_raw(pattern, path, glob) - - # === File Transfer === + for number, line in enumerate(self._bash.read_file(info["path"]).splitlines(), 1): + if pattern in line: + if max_count is not None and len(matches) >= max(0, max_count): + return GrepResult(matches=matches, truncated=True) + matches.append(GrepMatch(path=info["path"], line=number, text=line)) + except (RuntimeError, ValueError) as exc: + return GrepResult(error=str(exc), matches=matches or None, truncated=bool(matches)) + return GrepResult(matches=matches) def download_files(self, paths: list[str]) -> list[FileDownloadResponse]: responses = [] - for p in paths: - result = self._bash.execute_sync(f"cat {shlex.quote(p)}") - if result.exit_code == 0: - responses.append(FileDownloadResponse(path=p, content=result.stdout.encode(), error=None)) - else: - responses.append( - FileDownloadResponse(path=p, content=None, error=result.stderr or "File not found") - ) + for path in paths: + try: + content = self._bash.fs().read_file(self._path(path)) + responses.append(FileDownloadResponse(path=path, content=content)) + except (RuntimeError, ValueError) as exc: + responses.append(FileDownloadResponse(path=path, error=str(exc))) return responses - async def adownload_files(self, paths: list[str]) -> list[FileDownloadResponse]: - return self.download_files(paths) - def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]: responses = [] - for p, content in files: + for path, content in files: try: - wr = self.write(p, content.decode("utf-8")) - responses.append(FileUploadResponse(path=p, error=None if wr.success else wr.error)) - except UnicodeDecodeError: - responses.append(FileUploadResponse(path=p, error="Binary files not supported")) + absolute = self._path(path) + self._bash.mkdir(posixpath.dirname(absolute), recursive=True) + self._bash.fs().write_file(absolute, content) + responses.append(FileUploadResponse(path=path)) + except (RuntimeError, ValueError) as exc: + responses.append(FileUploadResponse(path=path, error=str(exc))) return responses - async def aupload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]: - return self.upload_files(files) - # === Utility === def setup(self, script: str) -> str: diff --git a/crates/bashkit-python/bashkit/langchain.py b/crates/bashkit-python/bashkit/langchain.py index 79d0bf79a..7c58cb601 100644 --- a/crates/bashkit-python/bashkit/langchain.py +++ b/crates/bashkit-python/bashkit/langchain.py @@ -36,7 +36,7 @@ from __future__ import annotations from collections.abc import Callable -from typing import Any +from typing import TYPE_CHECKING, Any try: from langchain_core.tools import BaseTool, ToolException @@ -45,14 +45,15 @@ LANGCHAIN_AVAILABLE = True except ImportError: LANGCHAIN_AVAILABLE = False - BaseTool = object - BaseModel = object + if not TYPE_CHECKING: + BaseTool = object + BaseModel = object - def Field(*args, **kwargs): - return None + def Field(*args, **kwargs): + return None - def PrivateAttr(*args, **kwargs): - return None + def PrivateAttr(*args, **kwargs): + return None from bashkit import BashTool as NativeBashTool diff --git a/crates/bashkit-python/pyproject.toml b/crates/bashkit-python/pyproject.toml index 9468ef9a7..6a30012ef 100644 --- a/crates/bashkit-python/pyproject.toml +++ b/crates/bashkit-python/pyproject.toml @@ -35,7 +35,7 @@ Changelog = "https://github.com/everruns/bashkit/releases" [project.optional-dependencies] langchain = ["langchain-core>=0.3", "langchain-anthropic>=0.3"] -deepagents = ["deepagents>=0.3.11", "langchain-anthropic>=0.3"] +deepagents = ["deepagents>=0.7.13", "langchain-anthropic>=0.3"] pydantic-ai = ["pydantic-ai>=1.0"] dev = ["pytest>=7.0", "pytest-asyncio>=0.23"] diff --git a/crates/bashkit-python/tests/_bashkit_categories.py b/crates/bashkit-python/tests/_bashkit_categories.py index e28bffa1b..060ec99aa 100644 --- a/crates/bashkit-python/tests/_bashkit_categories.py +++ b/crates/bashkit-python/tests/_bashkit_categories.py @@ -374,7 +374,7 @@ def load_file(): def test_bash_mounts_readonly_by_default(tmp_path): (tmp_path / "data.txt").write_text("original\n") - bash = Bash(mounts=[{"host_path": str(tmp_path), "vfs_path": "/data"}]) + bash = Bash(mounts=[{"host_path": str(tmp_path), "vfs_path": "/data"}], allowed_mount_paths=[str(tmp_path)]) # Can read assert bash.execute_sync("cat /data/data.txt").stdout == "original\n" # Write goes to in-memory overlay, host file unchanged @@ -383,7 +383,10 @@ def test_bash_mounts_readonly_by_default(tmp_path): def test_bash_mounts_writable(tmp_path): - bash = Bash(mounts=[{"host_path": str(tmp_path), "vfs_path": "/workspace", "writable": True}]) + bash = Bash( + mounts=[{"host_path": str(tmp_path), "vfs_path": "/workspace", "writable": True}], + allowed_mount_paths=[str(tmp_path)], + ) result = bash.execute_sync("echo 'hello host' > /workspace/hello.txt") assert result.exit_code == 0 assert (tmp_path / "hello.txt").read_text().strip() == "hello host" @@ -724,7 +727,10 @@ def generate_report(): def test_bashtool_realfs_and_fs_handle(tmp_path): - tool = BashTool(mounts=[{"host_path": str(tmp_path), "vfs_path": "/workspace", "writable": True}]) + tool = BashTool( + mounts=[{"host_path": str(tmp_path), "vfs_path": "/workspace", "writable": True}], + allowed_mount_paths=[str(tmp_path)], + ) tool.execute_sync("echo 'from tool' > /workspace/tool.txt") assert (tmp_path / "tool.txt").read_text().strip() == "from tool" assert tool.fs().read_file("/workspace/tool.txt") == b"from tool\n" diff --git a/crates/bashkit-python/tests/_security_advanced.py b/crates/bashkit-python/tests/_security_advanced.py index dd7d8e57c..243446569 100644 --- a/crates/bashkit-python/tests/_security_advanced.py +++ b/crates/bashkit-python/tests/_security_advanced.py @@ -1047,21 +1047,6 @@ def test_no_raw_string_formatting_in_methods(self): if "% (" in stripped and "path" in stripped: pytest.fail(f"L{i}: Potential %-format injection: {stripped}") - def test_write_cmd_preserves_content_integrity(self): - """_build_write_cmd must not corrupt special characters in content.""" - import importlib - - mod = importlib.import_module("bashkit.deepagents") - build = mod._build_write_cmd - - # Content with every dangerous character - content = "line1\n$HOME\n`whoami`\n$(id)\n'single'\n\"double\"\n\\backslash" - cmd = build("/tmp/test.txt", content) - # The heredoc uses single-quoted delimiter, so content is literal - assert "<<" in cmd - # Delimiter is single-quoted (no expansion) - assert "'" in cmd.split("<<")[1].split("\n")[0] - # =========================================================================== # 15. BLACK-BOX: ExecResult data integrity diff --git a/crates/bashkit-python/tests/test_ai_adapters.py b/crates/bashkit-python/tests/test_ai_adapters.py index 85ead3796..33a272cbb 100644 --- a/crates/bashkit-python/tests/test_ai_adapters.py +++ b/crates/bashkit-python/tests/test_ai_adapters.py @@ -86,59 +86,6 @@ def test_deepagents_all_exports(): assert "BashkitBackend" in __all__ -def test_deepagents_now_iso(): - """_now_iso returns ISO format string.""" - from bashkit.deepagents import _now_iso - - ts = _now_iso() - assert isinstance(ts, str) - assert "T" in ts # ISO format has T separator - - -def test_deepagents_write_heredoc_injection(): - """Content containing the heredoc delimiter must not cause injection.""" - from bashkit import BashTool - from bashkit.deepagents import _build_write_cmd - - # Content that would terminate a fixed BASHKIT_EOF heredoc early - malicious = "line1\nBASHKIT_EOF\necho INJECTED\nmore" - cmd = _build_write_cmd("/tmp/test_inject.txt", malicious) - - # The generated delimiter must not be the plain "BASHKIT_EOF" - # so content containing that literal cannot terminate it early - tool = BashTool() - tool.execute_sync(cmd) - r = tool.execute_sync("cat /tmp/test_inject.txt") - assert r.exit_code == 0 - # The file must contain the literal BASHKIT_EOF line, not execute it - assert "BASHKIT_EOF" in r.stdout - assert "INJECTED" not in r.stdout or "echo INJECTED" in r.stdout - # All original lines present - assert "line1" in r.stdout - assert "more" in r.stdout - - -def test_deepagents_write_cmd_uses_shlex_quote(): - """_build_write_cmd must quote file paths with special characters.""" - from bashkit.deepagents import _build_write_cmd - - cmd = _build_write_cmd("/tmp/my file.txt", "hello") - # shlex.quote wraps in single quotes for paths with spaces - assert "'/tmp/my file.txt'" in cmd - - -def test_deepagents_write_cmd_unique_delimiters(): - """Each call should produce a unique delimiter.""" - from bashkit.deepagents import _build_write_cmd - - cmd1 = _build_write_cmd("/tmp/a.txt", "x") - cmd2 = _build_write_cmd("/tmp/b.txt", "y") - # Extract delimiter from first line: cat > path << 'DELIM' - delim1 = cmd1.split("'")[-2] - delim2 = cmd2.split("'")[-2] - assert delim1 != delim2 - - # =========================================================================== # pydantic_ai.py tests # =========================================================================== diff --git a/crates/bashkit-python/tests/test_deepagents_backend.py b/crates/bashkit-python/tests/test_deepagents_backend.py new file mode 100644 index 000000000..7a64994c6 --- /dev/null +++ b/crates/bashkit-python/tests/test_deepagents_backend.py @@ -0,0 +1,151 @@ +"""Exercise the real Deep Agents protocol against Bashkit's native VFS.""" + +import pytest + +pytest.importorskip("deepagents") + +from deepagents.backends.protocol import GrepResult, ReadResult, SandboxBackendProtocol # noqa: E402 + +from bashkit.deepagents import BashkitBackend # noqa: E402 + + +@pytest.fixture +def backend(): + return BashkitBackend() + + +@pytest.mark.parametrize("redirect", ["", " >&2"]) +def test_execute_reports_native_truncation(backend, redirect): + response = backend.execute("printf '" + "x" * 1_100_000 + "'" + redirect) + assert response.truncated is True + assert len(response.output) == 1_048_576 + assert backend.execute("echo small").truncated is False + + +def test_current_protocol_file_operations(backend): + assert isinstance(backend, SandboxBackendProtocol) + assert backend.write("/tmp/file.txt", "one\ntwo\nthree").error is None + assert backend.write("/tmp/file.txt", "overwrite").error is not None + result = backend.read("/tmp/file.txt", offset=1, limit=1) + assert isinstance(result, ReadResult) + assert result.file_data == {"content": "two", "encoding": "utf-8"} + assert (result.start_line, result.end_line, result.next_offset, result.total_lines) == (2, 2, 2, 3) + assert backend.read("/tmp/file.txt", limit=0).no_lines_requested + assert backend.read("/tmp/file.txt", offset=-1, limit=1).file_data["content"] == "one" + assert backend.read("/missing").error is not None + edit = backend.edit("/tmp/file.txt", "two", "changed") + assert edit.error is None and edit.occurrences == 1 + assert backend.read("/tmp/file.txt").file_data["content"] == "one\nchanged\nthree" + assert backend.ls("/tmp").entries == [{"path": "/tmp/file.txt", "is_dir": False, "size": 17}] + assert backend.upload_files([("/tmp/upload.txt", b"exact")])[0].error is None + assert backend.download_files(["/tmp/upload.txt"])[0].content == b"exact" + assert backend.delete("/tmp/file.txt").error is None + assert backend.delete("/tmp/file.txt").error is not None + + +@pytest.mark.parametrize( + "glob,expected", + [ + ("*.py", {"top.py", "src/test1.py", "src/deep/test2.py"}), + ("src/**/*.py", {"src/test1.py", "src/deep/test2.py"}), + ("/*.py", {"top.py"}), + ("src/test[0-9].py", {"src/test1.py"}), + ("*.absent", set()), + ], +) +def test_grep_glob_and_literal_pattern(backend, glob, expected): + backend.setup("mkdir -p /tmp/project/src/deep; cd /tmp/project") + for path in ["top.py", "src/test1.py", "src/deep/test2.py", "src/test1.txt"]: + assert backend.write(path, "a.b\naXb\n").error is None + result = backend.grep("a.b", glob=glob) + assert isinstance(result, GrepResult) + assert result.error is None + assert {m["path"].removeprefix("/tmp/project/") for m in result.matches} == expected + assert all(m["line"] == 1 and m["text"] == "a.b" for m in result.matches) + assert {m["path"] for m in backend.glob(glob).matches} == {m["path"] for m in result.matches} + + +def test_grep_handles_paths_patterns_and_limits(backend, tmp_path): + host_only = tmp_path / "host-only.txt" + host_only.write_text("secret") + weird = "/tmp/a:b;$(touch injected).py" + assert backend.write("/tmp/invalid\nname.py", "no").error + assert backend.write(weird, "-needle\n-needle\n").error is None + result = backend.grep("-needle", "/tmp", glob="*.py", max_count=1) + assert result.matches == [{"path": weird, "line": 1, "text": "-needle"}] + assert result.truncated + assert not backend.grep("-needle", "/tmp", max_count=2).truncated + assert backend.grep("-needle", "/tmp", max_count=0).truncated + assert backend.grep("x", "/missing").error + assert backend.grep("x", "/tmp", glob="../*").error + assert backend.glob("../*", "/tmp").error + assert backend.read(str(host_only)).error + assert backend.execute("test -e injected").exit_code != 0 + backend.setup("ln -s /tmp /tmp/cycle") + assert backend.grep("-needle", "/tmp", glob="*.py").matches == [ + {"path": weird, "line": number, "text": "-needle"} for number in (1, 2) + ] + + +async def test_async_protocol_shares_middleware_vfs(backend): + tool = backend.create_middleware().tools[0] + assert "done" in tool.invoke({"command": "echo shared > /tmp/shared.txt; echo done"}) + assert (await backend.aread("/tmp/shared.txt")).file_data["content"] == "shared" + assert (await backend.agrep("shared", "/tmp")).matches[0]["line"] == 1 + assert (await backend.aglob("*.txt", "/tmp")).matches[0]["path"] == "/tmp/shared.txt" + assert (await backend.als("/tmp")).entries + assert (await backend.aexecute("echo async")).output == "async\n" + + +def test_framework_filesystem_tools(backend): + """Real agent middleware consumes the result contracts without an API call.""" + from deepagents import create_deep_agent + from langchain_core.language_models.fake_chat_models import FakeMessagesListChatModel + from langchain_core.messages import AIMessage + + class ScriptedModel(FakeMessagesListChatModel): + def bind_tools(self, tools, **kwargs): + return self + + calls = [ + ("write_file", {"file_path": "/tmp/protocol.py", "content": "needle\n"}), + ("read_file", {"file_path": "/tmp/protocol.py"}), + ("grep", {"pattern": "needle", "path": "/tmp", "glob": "*.py"}), + ("glob", {"pattern": "*.py", "path": "/tmp"}), + ("ls", {"path": "/tmp"}), + ("execute", {"command": "echo framework-execute"}), + ] + model = ScriptedModel( + responses=[ + AIMessage(content="", tool_calls=[{"name": name, "args": args, "id": str(i)}]) + for i, (name, args) in enumerate(calls) + ] + + [AIMessage(content="done")] + ) + agent = create_deep_agent(model=model, backend=backend) + response = agent.invoke({"messages": [{"role": "user", "content": "Exercise filesystem tools"}]}) + messages = [message for message in response["messages"] if message.type == "tool"] + assert len(messages) == len(calls) + assert all(message.status == "success" for message in messages) + assert "needle" in str(messages[1].content) + assert "/tmp/protocol.py" in str(messages[2].content) + assert "framework-execute" in str(messages[-1].content) + from deepagents.backends.protocol import execute_accepts_timeout + + assert not execute_accepts_timeout(type(backend)) + + +def test_binary_transfers_and_large_exact_file(backend): + content = bytes(range(256)) * 5000 + assert backend.upload_files([("/tmp/binary", content)])[0].error is None + assert backend.download_files(["/tmp/binary"])[0].content == content + assert backend.upload_files([("/tmp/binary", b"replacement")])[0].error is None + assert backend.download_files(["/tmp/binary"])[0].content == b"replacement" + assert backend.download_files(["/missing"])[0].error + assert backend.upload_files([("/tmp", b"no")])[0].error + assert backend.delete("/tmp").error + text = "x" * 1_100_000 + assert backend.write("/tmp/large.txt", text).error is None + assert backend.read("/tmp/large.txt").file_data["content"] == text + assert backend.edit("/tmp/large.txt", text, "short").error is None + assert backend.download_files(["/tmp/large.txt"])[0].content == b"short" diff --git a/crates/bashkit-python/tests/test_python_security.py b/crates/bashkit-python/tests/test_python_security.py index 7504c97c0..8d256538b 100644 --- a/crates/bashkit-python/tests/test_python_security.py +++ b/crates/bashkit-python/tests/test_python_security.py @@ -141,56 +141,6 @@ def test_multiple_resets_stable(self): assert "ok" in r.stdout -# =========================================================================== -# 4. WHITE-BOX: deepagents.py heredoc injection -# =========================================================================== - - -class TestHeredocInjection: - """Test that heredoc delimiter injection is prevented.""" - - def _get_build_write_cmd(self): - """Import _build_write_cmd from deepagents module.""" - import importlib - - mod = importlib.import_module("bashkit.deepagents") - return mod._build_write_cmd - - def test_fixed_delimiter_cant_be_injected(self): - build = self._get_build_write_cmd() - # Content tries to terminate the heredoc early - malicious = "BASHKIT_EOF\necho PWNED\nBASHKIT_EOF" - cmd = build("/tmp/test.txt", malicious) - # Delimiter should be randomized, not just BASHKIT_EOF - assert "BASHKIT_EOF_" in cmd - # The malicious BASHKIT_EOF in content won't match the random one - lines = cmd.splitlines() - delimiter = lines[0].split("'")[1] # Extract from << 'DELIM' - assert delimiter != "BASHKIT_EOF" - assert len(delimiter) > 20 # BASHKIT_EOF_ + 16 hex chars - - def test_delimiter_unique_per_call(self): - build = self._get_build_write_cmd() - cmd1 = build("/tmp/a.txt", "content") - cmd2 = build("/tmp/b.txt", "content") - delim1 = cmd1.splitlines()[0].split("'")[1] - delim2 = cmd2.splitlines()[0].split("'")[1] - assert delim1 != delim2, "Each call must use a unique delimiter" - - def test_path_is_quoted(self): - build = self._get_build_write_cmd() - cmd = build("/tmp/path with spaces/file.txt", "content") - assert "shlex" not in cmd # shlex.quote result, not the word shlex - # Path should be single-quoted by shlex.quote - assert "'/tmp/path with spaces/file.txt'" in cmd - - def test_malicious_path_quoted(self): - build = self._get_build_write_cmd() - cmd = build("/tmp/'; rm -rf /; echo '", "content") - # shlex.quote wraps in single quotes, escaping inner quotes - assert "rm -rf" not in cmd.split("\n")[0].split(">")[0] # Not in command part unquoted - - # =========================================================================== # 5. WHITE-BOX: deepagents.py shell injection via methods # =========================================================================== diff --git a/crates/bashkit-python/tests/test_shell_injection.py b/crates/bashkit-python/tests/test_shell_injection.py index 985905299..c6c4c15ba 100644 --- a/crates/bashkit-python/tests/test_shell_injection.py +++ b/crates/bashkit-python/tests/test_shell_injection.py @@ -1,104 +1,30 @@ -"""Tests for shell injection prevention in BashkitBackend (deepagents.py). - -Verifies that user-supplied paths and patterns are properly quoted -with shlex.quote() to prevent command injection via f-string interpolation. - -Ref: GitHub issue #411 -""" - -import shlex -from pathlib import Path - -# Read deepagents.py source directly (BashkitBackend only exists when -# deepagents is installed, so we inspect source text instead). -_DEEPAGENTS_SRC = (Path(__file__).resolve().parent.parent / "bashkit" / "deepagents.py").read_text() - - -# -- Module-level checks ----------------------------------------------------- - - -def test_shlex_imported_in_deepagents(): - """deepagents.py must import shlex for shell argument quoting.""" - assert "import shlex" in _DEEPAGENTS_SRC, "deepagents.py must import shlex" - - -def test_no_unquoted_cat_interpolation(): - """No raw f'cat {var}' patterns without shlex.quote.""" - # After fix, all cat uses should go through shlex.quote - for line in _DEEPAGENTS_SRC.splitlines(): - stripped = line.strip() - if stripped.startswith("#"): - continue - if 'f"cat {' in stripped or "f'cat {" in stripped: - if "shlex.quote" not in stripped: - assert False, f"Unquoted cat interpolation found: {stripped}" - - -def test_no_unquoted_ls_interpolation(): - """No raw f'ls -la {var}' patterns without shlex.quote.""" - for line in _DEEPAGENTS_SRC.splitlines(): - stripped = line.strip() - if stripped.startswith("#"): - continue - if 'f"ls ' in stripped and "{" in stripped and "shlex.quote" not in stripped: - assert False, f"Unquoted ls interpolation found: {stripped}" - - -def test_no_unquoted_find_interpolation(): - """No raw f'find {var}' patterns without shlex.quote.""" - for line in _DEEPAGENTS_SRC.splitlines(): - stripped = line.strip() - if stripped.startswith("#"): - continue - if 'f"find {' in stripped or "f'find {" in stripped: - if "shlex.quote" not in stripped: - assert False, f"Unquoted find interpolation found: {stripped}" - - -def test_no_unquoted_grep_interpolation(): - """grep_raw must use shlex.quote for pattern and path.""" - # Extract grep_raw method body and verify shlex.quote is used - in_grep_raw = False - grep_raw_lines = [] - for line in _DEEPAGENTS_SRC.splitlines(): - if "def grep_raw(" in line: - in_grep_raw = True - elif in_grep_raw and (line.strip().startswith("def ") or line.strip().startswith("async def ")): - break - if in_grep_raw: - grep_raw_lines.append(line) - grep_raw_body = "\n".join(grep_raw_lines) - assert "shlex.quote" in grep_raw_body, "grep_raw must use shlex.quote for pattern/path" - - -def test_shlex_quote_used_for_file_paths(): - """shlex.quote must appear in methods that interpolate file paths.""" - assert _DEEPAGENTS_SRC.count("shlex.quote") >= 7, ( - "Expected at least 7 uses of shlex.quote (read, write, edit, ls_info, glob_info, grep_raw, download_files)" - ) - - -# -- shlex.quote behavior validation ----------------------------------------- - - -def test_shlex_quote_prevents_semicolon_injection(): - """shlex.quote must neutralize semicolon-based injection.""" - malicious = "/dev/null; echo pwned" - quoted = shlex.quote(malicious) - # Quoted string wraps in single quotes, preventing shell interpretation - assert quoted.startswith("'"), "shlex.quote must single-quote dangerous input" - assert ";" in quoted # semicolon is inside quotes, not a command separator - - -def test_shlex_quote_prevents_backtick_injection(): - """shlex.quote must neutralize backtick-based injection.""" - malicious = "/tmp/`rm -rf /`/file" - quoted = shlex.quote(malicious) - assert quoted.startswith("'"), "shlex.quote must single-quote dangerous input" - - -def test_shlex_quote_prevents_dollar_expansion(): - """shlex.quote must neutralize $() command substitution.""" - malicious = "/tmp/$(cat /etc/passwd)/file" - quoted = shlex.quote(malicious) - assert quoted.startswith("'"), "shlex.quote must single-quote dangerous input" +"""File API arguments must never become shell source.""" + +import pytest + +pytest.importorskip("deepagents") + +from bashkit.deepagents import BashkitBackend # noqa: E402 + + +@pytest.mark.parametrize( + "path", + [ + "/tmp/path with spaces/file.txt", + "/tmp/a;touch INJECTED", + "/tmp/$(touch INJECTED)", + "/tmp/`touch INJECTED`", + "/tmp/'quoted'", + "/tmp/-option", + ], +) +def test_file_operations_preserve_literal_paths_and_contents(path): + backend = BashkitBackend() + malicious = "BASHKIT_EOF\ntouch INJECTED\n$HOME\n`whoami`\n$(id)\n'single'\n\"double\"\n\\backslash" + assert backend.write(path, malicious).error is None + assert backend.download_files([path])[0].content == malicious.encode() + assert backend.read(path).file_data["content"] == malicious + assert backend.edit(path, malicious, "$(touch INJECTED)").error is None + assert backend.download_files([path])[0].content == b"$(touch INJECTED)" + assert backend.grep("$(touch INJECTED)", path).matches == [{"path": path, "line": 1, "text": "$(touch INJECTED)"}] + assert backend.execute("test -e INJECTED").exit_code != 0 diff --git a/crates/bashkit-wasm/scripts/build.sh b/crates/bashkit-wasm/scripts/build.sh index 428ef3ad6..1984308ef 100755 --- a/crates/bashkit-wasm/scripts/build.sh +++ b/crates/bashkit-wasm/scripts/build.sh @@ -4,7 +4,7 @@ # Produces an ES-module package under `pkg/` via `wasm-bindgen --target web`, # which needs NO bundler and NO cross-origin isolation headers to load. # -# Requires: rustup target add wasm32-unknown-unknown; cargo install wasm-bindgen-cli +# Requires: rustup target add wasm32-unknown-unknown; bash scripts/install-wasm-bindgen.sh # Optional (smaller output): wasm-opt (binaryen) on PATH. set -euo pipefail diff --git a/crates/bashkit/Cargo.toml b/crates/bashkit/Cargo.toml index bf71330a2..bfb9bcfe4 100644 --- a/crates/bashkit/Cargo.toml +++ b/crates/bashkit/Cargo.toml @@ -114,16 +114,22 @@ os_display = "0.1.3" # exact pin; the matching `ignore` entries in .github/dependabot.yml stop the # bump PRs from being regenerated. # -# 0.0.21 moves `max_memory` enforcement from host-side accounting to a probe of +# 0.0.20–0.0.22 move `max_memory` enforcement from host-side accounting to a probe of # the `LIVE_MEMORY`/`BASELINE_MEMORY` globals, which only the separate # `monty-alloc` crate writes — and only when installed as the process-wide # global allocator. Bashkit is an embeddable library (and bashkit-python is a # CPython extension module), so it cannot impose a global allocator on its # hosts. Without one, `probe_memory()` is `0.saturating_sub(usize::MAX)` == 0 # and the Python sandbox's memory ceiling silently stops being enforced. +# These versions also remove the ResourceTracker trait used for shared work +# and cancellation checkpoints; charging only around start/resume loses that +# enforcement during an uninterrupted VM loop. # See knowledge/runtimes/python-builtin.md § Upgrade blocker. -monty = { version = "0.0.19", optional = true } -monty-types = { version = "0.0.19", optional = true } +monty = { version = "=0.0.19", optional = true } +monty-types = { version = "=0.0.19", optional = true } +# Monty's ruff AST uses compact_str 0.9; get-size2 >=0.10.2 only implements +# GetSize for compact_str 0.10. Enforce downstream too, not only in Cargo.lock. +get-size2 = { version = "=0.10.1", optional = true } # Embedded TypeScript interpreter (optional) zapcode-core = { version = "1.5.1", optional = true } @@ -210,7 +216,7 @@ ssh = ["russh"] scripted_tool = ["bash_tool"] # Enable python/python3 builtins via embedded Monty interpreter # Usage: cargo build --features python -python = ["dep:monty", "dep:monty-types"] +python = ["dep:monty", "dep:monty-types", "dep:get-size2"] # Enable ts/node/deno/bun builtins via embedded ZapCode TypeScript interpreter # Usage: cargo build --features typescript typescript = ["dep:zapcode-core"] diff --git a/crates/bashkit/benches/results/criterion-parallel-Mykhailos-Mac-mini.local-darwin-arm64-1788628263.md b/crates/bashkit/benches/results/criterion-parallel-Mykhailos-Mac-mini.local-darwin-arm64-1788628263.md new file mode 100644 index 000000000..8382ef3db --- /dev/null +++ b/crates/bashkit/benches/results/criterion-parallel-Mykhailos-Mac-mini.local-darwin-arm64-1788628263.md @@ -0,0 +1,75 @@ +# Criterion Parallel Execution Benchmark + +## System Information + +- **Moniker**: `Mykhailos-Mac-mini.local-darwin-arm64` +- **Hostname**: Mykhailos-Mac-mini.local +- **OS**: darwin +- **Architecture**: arm64 +- **CPUs**: ? +- **Timestamp**: 1788628263 + +## Workload Comparison (50 sessions) + +| Benchmark | Time | +|-----------|------| +| workload_types/light_sequential | 1.5995 ms | +| workload_types/light_parallel | 1.6871 ms | +| workload_types/medium_sequential | 7.1423 ms | +| workload_types/medium_parallel | 3.0198 ms | +| workload_types/heavy_sequential | 25.355 ms | +| workload_types/heavy_parallel | 7.1101 ms | + +## Parallel Scaling (medium workload) + +| Benchmark | Time | +|-----------|------| +| parallel_scaling/medium_seq/10 | 1.7688 ms | +| parallel_scaling/medium_par/10 | 490.81 µs | +| parallel_scaling/shared_fs/10 | 298.25 µs | +| parallel_scaling/medium_seq/50 | 6.7227 ms | +| parallel_scaling/medium_par/50 | 1.8625 ms | +| parallel_scaling/shared_fs/50 | 1.0918 ms | +| parallel_scaling/medium_seq/100 | 13.412 ms | +| parallel_scaling/medium_par/100 | 3.5558 ms | +| parallel_scaling/shared_fs/100 | 2.0785 ms | +| parallel_scaling/medium_seq/200 | 26.646 ms | +| parallel_scaling/medium_par/200 | 6.9718 ms | +| parallel_scaling/shared_fs/200 | 5.1099 ms | +| parallel_scaling/medium_seq/500 | 73.207 ms | +| parallel_scaling/medium_par/500 | 18.433 ms | +| parallel_scaling/shared_fs/500 | 13.094 ms | +| parallel_scaling/medium_seq/1000 | 140.23 ms | +| parallel_scaling/medium_par/1000 | 39.202 ms | +| parallel_scaling/shared_fs/1000 | 30.817 ms | + +## Single Operations + +| Benchmark | Time | +|-----------|------| +| single_bash_new | 12.553 µs | +| single_echo | 16.711 µs | +| single_file_write_read | 25.404 µs | +| single_grep | 24.181 µs | +| single_awk | 24.413 µs | +| single_sed | 58.494 µs | +| single_light_script | 28.181 µs | +| single_medium_script | 136.97 µs | +| single_heavy_script | 465.35 µs | + +## Speedup Summary + +| Workload | Sequential | Parallel | Speedup | +|----------|-----------|----------|---------| +| light | 1.599 ms | 1.687 ms | **0.95x** | +| medium | 7.142 ms | 3.020 ms | **2.37x** | +| heavy | 25.355 ms | 7.110 ms | **3.57x** | + +| Sessions | Sequential | Parallel | Shared FS | Par Speedup | +|----------|-----------|----------|-----------|-------------| +| 10 | 1.769 ms | 0.491 ms | 0.298 ms | **3.60x** | +| 50 | 6.723 ms | 1.863 ms | 1.092 ms | **3.61x** | +| 100 | 13.412 ms | 3.556 ms | 2.079 ms | **3.77x** | +| 200 | 26.646 ms | 6.972 ms | 5.110 ms | **3.82x** | +| 500 | 73.207 ms | 18.433 ms | 13.094 ms | **3.97x** | +| 1000 | 140.230 ms | 39.202 ms | 30.817 ms | **3.58x** | diff --git a/crates/bashkit/benches/results/criterion-sqlite-Mykhailos-Mac-mini.local-darwin-arm64-1788628889.md b/crates/bashkit/benches/results/criterion-sqlite-Mykhailos-Mac-mini.local-darwin-arm64-1788628889.md new file mode 100644 index 000000000..c871e790d --- /dev/null +++ b/crates/bashkit/benches/results/criterion-sqlite-Mykhailos-Mac-mini.local-darwin-arm64-1788628889.md @@ -0,0 +1,89 @@ +# Criterion SQLite Builtin Benchmark + +Measures the `sqlite` builtin (Turso embedded engine) end-to-end through +the bashkit interpreter. Per-invocation overhead (interpreter setup, script +parse, engine open, VFS flush) is included in every number — these are +"what a script author observes", not isolated engine micro-benchmarks. + +## System Information + +- **Moniker**: `Mykhailos-Mac-mini.local-darwin-arm64` +- **Hostname**: Mykhailos-Mac-mini.local +- **OS**: darwin +- **Architecture**: arm64 +- **CPUs**: ? +- **Timestamp**: 1788628889 + +## CRUD (insert / update, Memory vs Vfs backend, n rows) + +| Benchmark | Time | +|-----------|------| +| sqlite_crud/insert_mem/100 | 376.52 µs | +| sqlite_crud/insert_vfs/100 | 454.23 µs | +| sqlite_crud/update_mem/100 | 393.30 µs | +| sqlite_crud/update_vfs/100 | 469.39 µs | +| sqlite_crud/insert_mem/1000 | 1.2050 ms | +| sqlite_crud/insert_vfs/1000 | 1.2952 ms | +| sqlite_crud/update_mem/1000 | 1.3168 ms | +| sqlite_crud/update_vfs/1000 | 1.4104 ms | +| sqlite_crud/insert_mem/10000 | 9.5156 ms | +| sqlite_crud/insert_vfs/10000 | 9.6216 ms | +| sqlite_crud/update_mem/10000 | 10.540 ms | +| sqlite_crud/update_vfs/10000 | 10.528 ms | + +## Indexing (create index, indexed lookup, full scan) + +| Benchmark | Time | +|-----------|------| +| sqlite_index/create_index_mem/100 | 461.62 µs | +| sqlite_index/indexed_lookup_mem/100 | 551.89 µs | +| sqlite_index/full_scan_mem/100 | 383.43 µs | +| sqlite_index/create_index_mem/1000 | 1.8613 ms | +| sqlite_index/indexed_lookup_mem/1000 | 1.9269 ms | +| sqlite_index/full_scan_mem/1000 | 1.2768 ms | +| sqlite_index/create_index_mem/10000 | 17.195 ms | +| sqlite_index/indexed_lookup_mem/10000 | 17.333 ms | +| sqlite_index/full_scan_mem/10000 | 10.251 ms | + +## Query (GROUP BY aggregate) + +| Benchmark | Time | +|-----------|------| +| sqlite_query/aggregate_mem/100 | 413.84 µs | +| sqlite_query/aggregate_vfs/100 | 512.10 µs | +| sqlite_query/aggregate_in_memory/100 | 370.97 µs | +| sqlite_query/aggregate_mem/1000 | 1.4284 ms | +| sqlite_query/aggregate_vfs/1000 | 1.9106 ms | +| sqlite_query/aggregate_in_memory/1000 | 1.9607 ms | +| sqlite_query/aggregate_mem/10000 | 12.167 ms | +| sqlite_query/aggregate_vfs/10000 | 11.430 ms | +| sqlite_query/aggregate_in_memory/10000 | 9.4998 ms | + +## Output mode formatters (1k rows) + +| Benchmark | Time | +|-----------|------| +| sqlite_output_mode/list | 1.5921 ms | +| sqlite_output_mode/csv | 1.5973 ms | +| sqlite_output_mode/json | 1.5124 ms | +| sqlite_output_mode/markdown | 1.8245 ms | +| sqlite_output_mode/box | 1.8314 ms | + +## Persistence (cost per invocation) + +| Benchmark | Time | +|-----------|------| +| sqlite_persistence/two_invocations_mem | 1.2210 ms | +| sqlite_persistence/two_invocations_vfs | 1.4615 ms | +| sqlite_persistence/memory_db_baseline | 1.1896 ms | + +## Parallel sessions (N concurrent over shared VFS) + +| Benchmark | Time | +|-----------|------| +| sqlite_parallel/mem/4 | 1.1094 ms | +| sqlite_parallel/vfs/4 | 1.3285 ms | +| sqlite_parallel/mem/16 | 2.8895 ms | +| sqlite_parallel/vfs/16 | 3.2371 ms | +| sqlite_parallel/mem/64 | 10.171 ms | +| sqlite_parallel/vfs/64 | 11.525 ms | diff --git a/crates/bashkit/docs/compatibility.md b/crates/bashkit/docs/compatibility.md index c29044df8..f0e5f36a2 100644 --- a/crates/bashkit/docs/compatibility.md +++ b/crates/bashkit/docs/compatibility.md @@ -382,9 +382,10 @@ Default limits (configurable): | Resource | Default | Notes | |----------|---------|-------| | Commands | 10,000 | Per execution | -| Loop iterations | 100,000 | Per loop | +| Loop iterations | 10,000 | Per loop | +| Total loop iterations | 1,000,000 | Per execution | | Function depth | 100 | Recursion limit | -| Output size | 10MB | Total stdout | +| Output size | 1MiB each | Separate stdout and stderr caps | | Parser timeout | 5s | Prevents infinite parse | | Parser operations | 100,000 | Fuel-based limit | | Input size | 10MB | Max script size | diff --git a/crates/bashkit/docs/threat-model.md b/crates/bashkit/docs/threat-model.md index d97f83a8d..f2c4da756 100644 --- a/crates/bashkit/docs/threat-model.md +++ b/crates/bashkit/docs/threat-model.md @@ -277,7 +277,7 @@ Scripts may attempt to leak sensitive information. | Threat | Attack Example | Mitigation | Status | |--------|---------------|------------|--------| -| Fork-PR secret exfil (TM-INF-026) | Fork PR edits `examples/*.rs` or `build.rs` to read `$DOPPLER_TOKEN` / `$ANTHROPIC_API_KEY` from the runner env and exfiltrate; first-time-contributor approval gate runs the workflow from PR head, and `DOPPLER_TOKEN` is the master key to every other secret in the Doppler config | Three-layer fix in `.github/workflows/{ci,js,publish-js}.yml`: (1) fork-guard gates secret-using steps on `head.repo.fork != true`, (2) job-level secrets removed, each step sets only the tokens it needs, (3) `doppler run --only-secrets ` limits each invocation to the single secret the script reads. `ANTHROPIC_API_KEY` migrated off GH Actions secrets onto Doppler so `DOPPLER_TOKEN` is the only secret CI needs from GitHub | **FIXED** | +| Fork-PR secret exfil (TM-INF-026) | Fork PR edits `examples/*.rs` or `build.rs` to read `$DOPPLER_TOKEN` / `$ANTHROPIC_API_KEY` from the runner env and exfiltrate; first-time-contributor approval gate runs the workflow from PR head, and `DOPPLER_TOKEN` is the master key to every other secret in the Doppler config | Trusted-run gates exclude PR execution from secret-backed examples. Dedicated fetch steps in `.github/workflows/{ci,js,publish-js}.yml` request one API key, mask it, and exit before build/example execution. Execution steps receive only that scoped key; Docker inherits it by name, without a secret value in argv. Neither the execution shell nor a live Doppler parent retains the broad service token. This protects process environments on a trusted runner; it does not sandbox dependencies persisting across steps. Workflow-script regression tests check child/parent environments. CI has contents-read permission only and does not persist checkout credentials; release examples install the reviewed lockfile without lifecycle scripts before linking the built artifact | **FIXED** | **Additional information-disclosure hardening:** diff --git a/crates/bashkit/fuzz/Cargo.lock b/crates/bashkit/fuzz/Cargo.lock index 09a4969e8..48c45f42c 100644 --- a/crates/bashkit/fuzz/Cargo.lock +++ b/crates/bashkit/fuzz/Cargo.lock @@ -26,56 +26,12 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - [[package]] name = "anstyle" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys", -] - [[package]] name = "anyhow" version = "1.0.104" @@ -134,8 +90,7 @@ dependencies = [ "flate2", "futures-core", "futures-util", - "getrandom", - "gloo-timers", + "getrandom 0.4.3", "hmac", "jaq-core", "jaq-json", @@ -144,7 +99,7 @@ dependencies = [ "num-traits", "os_display", "regex", - "send_wrapper", + "rustls", "serde", "serde_json", "serde_yaml_ng", @@ -155,7 +110,6 @@ dependencies = [ "tower", "unit-prefix", "url", - "web-time", ] [[package]] @@ -273,9 +227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", - "js-sys", "num-traits", - "wasm-bindgen", "windows-link", ] @@ -305,10 +257,8 @@ version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ - "anstream", "anstyle", "clap_lex", - "strsim", ] [[package]] @@ -335,12 +285,6 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - [[package]] name = "const-oid" version = "0.10.2" @@ -497,32 +441,12 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures-channel" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" -dependencies = [ - "futures-core", -] - [[package]] name = "futures-core" version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" -[[package]] -name = "futures-macro" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.3", -] - [[package]] name = "futures-task" version = "0.3.34" @@ -536,7 +460,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", - "futures-macro", "futures-task", "pin-project-lite", "slab", @@ -544,27 +467,24 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.3" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", - "r-efi", - "wasm-bindgen", + "wasi", ] [[package]] -name = "gloo-timers" -version = "0.4.0" +name = "getrandom" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "482ce8a491a501da4cd806bd190275363d674f2845005c6ddbd5d3e1dd54495d" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", + "cfg-if", + "libc", + "r-efi", ] [[package]] @@ -740,12 +660,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - [[package]] name = "itoa" version = "1.0.18" @@ -858,7 +772,7 @@ version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom", + "getrandom 0.4.3", "libc", ] @@ -973,12 +887,6 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - [[package]] name = "os_display" version = "0.1.4" @@ -1101,6 +1009,55 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys", +] + +[[package]] +name = "rustls" +version = "0.23.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.23" @@ -1119,15 +1076,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" -dependencies = [ - "futures-core", -] - [[package]] name = "serde" version = "1.0.229" @@ -1243,10 +1191,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] -name = "strsim" -version = "0.11.1" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -1401,6 +1349,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.8" @@ -1426,10 +1380,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "utf8parse" -version = "0.2.2" +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" @@ -1476,16 +1430,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "windows-core" version = "0.62.2" @@ -1547,13 +1491,77 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.61.2" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-link", + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "writeable" version = "0.6.3" @@ -1604,6 +1612,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + [[package]] name = "zerotrie" version = "0.2.4" diff --git a/deny.toml b/deny.toml index 618a0f538..3d3358341 100644 --- a/deny.toml +++ b/deny.toml @@ -62,6 +62,13 @@ multiple-versions = "warn" # Deny specific crates (security or legal issues) deny = [] +# Jiter's local audit covers Monty's Rust parser features only. Its optional +# Python ASCII allocator dereferences PyUnicode_New without checking for OOM. +[[bans.features]] +name = "jiter" +deny = ["python"] +reason = "The local jiter audit excludes the optional Python FFI; re-audit before enabling it." + [sources] # Ensure all dependencies come from crates.io or known Git repos unknown-registry = "deny" diff --git a/examples/bashkit-pi/package.json b/examples/bashkit-pi/package.json index 01bcf42d5..8e0935c26 100644 --- a/examples/bashkit-pi/package.json +++ b/examples/bashkit-pi/package.json @@ -6,6 +6,6 @@ "packageManager": "pnpm@10.33.0", "description": "Pi coding agent with bashkit virtual bash + VFS", "dependencies": { - "@everruns/bashkit": "latest" + "@everruns/bashkit": "0.17.1" } } diff --git a/examples/bashkit-pi/pnpm-lock.yaml b/examples/bashkit-pi/pnpm-lock.yaml index 7610eb71c..6ede4f2a2 100644 --- a/examples/bashkit-pi/pnpm-lock.yaml +++ b/examples/bashkit-pi/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@everruns/bashkit': - specifier: latest - version: 0.16.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + specifier: 0.17.1 + version: 0.17.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) packages: @@ -23,8 +23,8 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@everruns/bashkit@0.16.0': - resolution: {integrity: sha512-Zafi1lGT209yEq022zaT82eA54flCJpyBq1lm67MEQU2NGUobKHMKXbtgIrcwbdlwULqQykvhdXJOABB0GNs+A==} + '@everruns/bashkit@0.17.1': + resolution: {integrity: sha512-nI1dtBRup+3MtC/r09A60mSSBXhqGLAUGzM/FhFt0N43Dy4YjOuJtttraMjKWXE3qgXknEt05wvOYTDFNqTVwQ==} engines: {node: '>= 18'} peerDependencies: '@langchain/core': '>=0.3' @@ -66,7 +66,7 @@ snapshots: tslib: 2.8.1 optional: true - '@everruns/bashkit@0.16.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@everruns/bashkit@0.17.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': optionalDependencies: '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) transitivePeerDependencies: diff --git a/examples/browser/browser-local.js b/examples/browser/browser-local.js index f87c69d27..fe897a5de 100644 --- a/examples/browser/browser-local.js +++ b/examples/browser/browser-local.js @@ -1,4 +1,5 @@ // browserLocal persists the browser example's /home/user tree in localStorage. +// Commit only complete traversals; read errors must not replace the last good save. const DEFAULT_KEY = "bashkit:fs"; const DEFAULT_ROOT = "/home/user"; const FORMAT_VERSION = 1; @@ -63,9 +64,10 @@ export function browserLocal({ storage: configuredStorage, key = DEFAULT_KEY, ro save(fs) { const files = {}; try { - snapshotDirectory(fs, root, files); + // An explicitly deleted root is a complete empty snapshot. + if (fs.exists(root)) snapshotDirectory(fs, root, files); } catch { - // The persisted root may have been removed by the last command. + return false; } try { diff --git a/examples/browser/browser-local.test.js b/examples/browser/browser-local.test.js index 914bfbeaf..d4c5efed7 100644 --- a/examples/browser/browser-local.test.js +++ b/examples/browser/browser-local.test.js @@ -21,6 +21,7 @@ class MemoryStorage { function fakeFs(tree) { return { + exists(path) { return Object.hasOwn(tree, path); }, ls(path) { const value = tree[path]; if (!Array.isArray(value)) throw new Error("not a directory"); @@ -159,3 +160,26 @@ test("rejects unsafe persisted paths", () => { "/home/user/ok.txt": "ok", }); }); + +for (const unreadable of ["binary", "directory"]) { + test(`preserves previous snapshot when ${unreadable} traversal fails`, () => { + const storage = new MemoryStorage(); + const previous = JSON.stringify({ version: 1, files: { "/home/user/old.txt": "keep" } }); + storage.setItem("bashkit:fs", previous); + const fs = fakeFs({ + "/home/user": ["first.txt", "unreadable"], + "/home/user/first.txt": "new partial data", + "/home/user/unreadable": unreadable === "binary" ? new Uint8Array([255]) : ["child"], + }); + assert.equal(browserLocal({ storage }).save(fs), false); + assert.equal(storage.getItem("bashkit:fs"), previous); + }); +} + +test("preserves previous snapshot when the existing root cannot be listed", () => { + const storage = new MemoryStorage(); + storage.setItem("bashkit:fs", "previous snapshot"); + const fs = fakeFs({ "/home/user": new Error("permission denied") }); + assert.equal(browserLocal({ storage }).save(fs), false); + assert.equal(storage.getItem("bashkit:fs"), "previous snapshot"); +}); diff --git a/examples/browser/package.json b/examples/browser/package.json index e8e451e2a..af7de76ca 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -10,7 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@everruns/bashkit-wasm": "0.16.0" + "@everruns/bashkit-wasm": "0.17.1" }, "devDependencies": { "vite": "8.2.2" diff --git a/examples/browser/pnpm-lock.yaml b/examples/browser/pnpm-lock.yaml index 156e10b4b..619812b97 100644 --- a/examples/browser/pnpm-lock.yaml +++ b/examples/browser/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: .: dependencies: '@everruns/bashkit-wasm': - specifier: 0.16.0 - version: 0.16.0 + specifier: 0.17.1 + version: 0.17.1 devDependencies: vite: specifier: 8.2.2 @@ -21,8 +21,8 @@ importers: packages: - '@everruns/bashkit-wasm@0.16.0': - resolution: {integrity: sha512-6Lx6tbZwZ9qHUsXDdNdYgXhjQFf8WTnDkJM0wVnIauEQ/u2v4bbl6AVIBZzR+4ndkGlA5njKYP2+j8Esq05OVg==} + '@everruns/bashkit-wasm@0.17.1': + resolution: {integrity: sha512-wAtZR7OOC/EqVik6WOXOiXfP1Zwm41XjNtCVtKQ33X47PpQCuy/qT4prJNcwnWk5cnNQwnXJyiCfND6mnIO1aA==} '@oxc-project/types@0.146.0': resolution: {integrity: sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==} @@ -292,7 +292,7 @@ packages: snapshots: - '@everruns/bashkit-wasm@0.16.0': {} + '@everruns/bashkit-wasm@0.17.1': {} '@oxc-project/types@0.146.0': {} diff --git a/examples/package.json b/examples/package.json index 8a00c8b71..28cfac9dd 100644 --- a/examples/package.json +++ b/examples/package.json @@ -9,13 +9,13 @@ "@everruns/bashkit": "latest" }, "devDependencies": { - "@ai-sdk/openai": "^4.0.50", + "@ai-sdk/openai": "^4.0.56", "@langchain/core": "^1.2.9", "@langchain/langgraph": "^1.4.13", - "@langchain/openai": "^1.5.10", - "ai": "^7.0.83", - "openai": "^7.8.0", - "zod": "^4.4.3" + "@langchain/openai": "^1.5.11", + "ai": "^7.0.90", + "openai": "^7.9.0", + "zod": "^4.5.4" }, "pnpm": { "overrides": { diff --git a/examples/pnpm-lock.yaml b/examples/pnpm-lock.yaml index 3e08a27b3..f4f15bc98 100644 --- a/examples/pnpm-lock.yaml +++ b/examples/pnpm-lock.yaml @@ -15,52 +15,52 @@ importers: dependencies: '@everruns/bashkit': specifier: latest - version: 0.17.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)))(zod@4.4.3) + version: 0.17.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)))(zod@4.5.4) devDependencies: '@ai-sdk/openai': - specifier: ^4.0.50 - version: 4.0.50(zod@4.4.3) + specifier: ^4.0.56 + version: 4.0.56(zod@4.5.4) '@langchain/core': specifier: ^1.2.9 - version: 1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)) + version: 1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)) '@langchain/langgraph': specifier: ^1.4.13 - version: 1.4.13(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)))(zod@4.4.3) + version: 1.4.13(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)))(zod@4.5.4) '@langchain/openai': - specifier: ^1.5.10 - version: 1.5.10(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)))(undici@7.29.0) + specifier: ^1.5.11 + version: 1.5.11(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)))(undici@7.29.0) ai: - specifier: ^7.0.83 - version: 7.0.83(zod@4.4.3) + specifier: ^7.0.90 + version: 7.0.90(zod@4.5.4) openai: - specifier: ^7.8.0 - version: 7.8.0(undici@7.29.0)(zod@4.4.3) + specifier: ^7.9.0 + version: 7.9.0(undici@7.29.0)(zod@4.5.4) zod: - specifier: ^4.4.3 - version: 4.4.3 + specifier: ^4.5.4 + version: 4.5.4 packages: - '@ai-sdk/gateway@4.0.67': - resolution: {integrity: sha512-LtyxLkg7dZ2iz8Ouh1806BJbA+q+FKc/mXUCl4v/wdNNIGtbfk80dNtlhqjhqOZa4dnfc3caVafRL7ocxzoegA==} + '@ai-sdk/gateway@4.0.72': + resolution: {integrity: sha512-iNNgagJ0+Ow1kdOFSyKPRk8kTh7qwhLZPLFdbXw4KMbMMF7JLzWlBSkyMSjh6U4O6D4D+76xj0HGws4JTLNeYA==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai@4.0.50': - resolution: {integrity: sha512-e2Jepw5RbSwsCsPwyiNzdlmOHoCQefCziNp3k1UxfIv8QD0/p4SF6+cx7GuWX98jAVNiWLjRv1W3NVSmx3FuEQ==} + '@ai-sdk/openai@4.0.56': + resolution: {integrity: sha512-q/GyXWeYUJFk76JIlgwp1yMEvEFPFT/ueKHTazbYI+tQ/8ZTTTdkL1y6bWN06zCIusbpt7laiKytFV+37JZsgA==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@5.0.32': - resolution: {integrity: sha512-MZUhlINn6FzKIWuX3T36h+yM9d7bG+yatH+kC99ZCe0DHxXfP73KwaoLiLcZDPQDamFyO3umPPBLJieZJyG4DQ==} + '@ai-sdk/provider-utils@5.0.36': + resolution: {integrity: sha512-MFXBn6XDyf37PNQAge/HTatPJE8Vmg/g/w4WPtjSV53jq8FKAzoaN5+43hsdQa9bqgN+/13jxug9ChvsG+godQ==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider@4.0.8': - resolution: {integrity: sha512-aWO7iwhFUGf347tCwNGggggfmZigaSu7TF739IZSrWWABUp7zkb4Cr3fMqvBe5EIS7ABJJu3Cadn0g/zs1G0QQ==} + '@ai-sdk/provider@4.0.10': + resolution: {integrity: sha512-fX2ENAc7iDpZ+Wp4+Rk06Usn/Ys7dI9uAkGv0jlF6XVrW13NkRWx5Ou+U6lIM2E1fTLkCs16GGrUAaVvroag7A==} engines: {node: '>=22'} '@cfworker/json-schema@4.1.1': @@ -116,8 +116,8 @@ packages: '@langchain/core': ^1.1.48 zod: ^3.25.32 || ^4.2.0 - '@langchain/openai@1.5.10': - resolution: {integrity: sha512-4cxdgolkkXwnAiGEkNrue+ba7jUKjfBwleLCX5DrRVcRGrCc4w5EceblYZOIaHMY6+nhwMqIOtSzBWgcBCLfmw==} + '@langchain/openai@1.5.11': + resolution: {integrity: sha512-BvGp5lQk5//0WVwTIepscazFpneT9I9+mc+kp+cLuhGHFb7mc9zGNrusZOXoa3p73SN0i3XqTo8lyIndpVx3Hw==} engines: {node: '>=22'} peerDependencies: '@langchain/core': ^1.2.9 @@ -155,8 +155,8 @@ packages: '@workflow/serde@4.1.0': resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} - ai@7.0.83: - resolution: {integrity: sha512-bg7+SopUwqA7DeQ2O8I9qELyQTHCeeI/0RuNUlT/gGz+LqWrIl5vbYRQv3eMBEnUsVFaM33n6SA8Vqe9gi8L1w==} + ai@7.0.90: + resolution: {integrity: sha512-cvG7Od3DPlnnljkIXP1CQ8wABOfDqV1uBFWemBjGupiqcQD8O9SSg5m4FL+GiMA47NN7dxIJnaw8/uowwN+D2g==} engines: {node: '>=22'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -208,8 +208,8 @@ packages: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true - openai@7.8.0: - resolution: {integrity: sha512-/2g9JzdnXNcjX1W/UlSNu+OdSFDAaAVt0n9Onom0kPenH54o59G2WrX/xjTnr26UHNSh6hxcAf58doGYRme2rw==} + openai@7.9.0: + resolution: {integrity: sha512-Qfx4qKmPllnilbuP6NgEj5KPUxLShYxrlRPWh3ZRQgNgs5B1V52PrfAdEQbW9MTGq7MquDYFMm+K/KXxiY736w==} engines: {node: '>=22.0.0'} peerDependencies: '@aws-sdk/credential-provider-node': '>=3.972.0 <4' @@ -263,34 +263,34 @@ packages: resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zod@4.5.4: + resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} snapshots: - '@ai-sdk/gateway@4.0.67(zod@4.4.3)': + '@ai-sdk/gateway@4.0.72(zod@4.5.4)': dependencies: - '@ai-sdk/provider': 4.0.8 - '@ai-sdk/provider-utils': 5.0.32(zod@4.4.3) + '@ai-sdk/provider': 4.0.10 + '@ai-sdk/provider-utils': 5.0.36(zod@4.5.4) '@vercel/oidc': 3.2.0 - zod: 4.4.3 + zod: 4.5.4 - '@ai-sdk/openai@4.0.50(zod@4.4.3)': + '@ai-sdk/openai@4.0.56(zod@4.5.4)': dependencies: - '@ai-sdk/provider': 4.0.8 - '@ai-sdk/provider-utils': 5.0.32(zod@4.4.3) - zod: 4.4.3 + '@ai-sdk/provider': 4.0.10 + '@ai-sdk/provider-utils': 5.0.36(zod@4.5.4) + zod: 4.5.4 - '@ai-sdk/provider-utils@5.0.32(zod@4.4.3)': + '@ai-sdk/provider-utils@5.0.36(zod@4.5.4)': dependencies: - '@ai-sdk/provider': 4.0.8 + '@ai-sdk/provider': 4.0.10 '@standard-schema/spec': 1.1.0 '@workflow/serde': 4.1.0 eventsource-parser: 3.1.1 undici: 7.29.0 - zod: 4.4.3 + zod: 4.5.4 - '@ai-sdk/provider@4.0.8': + '@ai-sdk/provider@4.0.10': dependencies: json-schema: 0.4.0 @@ -312,24 +312,24 @@ snapshots: tslib: 2.8.1 optional: true - '@everruns/bashkit@0.17.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)))(zod@4.4.3)': + '@everruns/bashkit@0.17.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)))(zod@4.5.4)': optionalDependencies: - '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)) + '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)) '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - zod: 4.4.3 + zod: 4.5.4 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' - '@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3))': + '@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4))': dependencies: '@cfworker/json-schema': 4.1.1 '@standard-schema/spec': 1.1.0 js-tiktoken: 1.0.21 - langsmith: 0.6.3(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)) + langsmith: 0.6.3(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)) mustache: 4.2.0 p-queue: 6.6.2 - zod: 4.4.3 + zod: 4.5.4 transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/exporter-trace-otlp-proto' @@ -337,36 +337,36 @@ snapshots: - openai - ws - '@langchain/langgraph-checkpoint@1.1.5(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)))': + '@langchain/langgraph-checkpoint@1.1.5(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)))': dependencies: - '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)) + '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)) - '@langchain/langgraph-sdk@1.10.0(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)))': + '@langchain/langgraph-sdk@1.10.0(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)))': dependencies: - '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)) + '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)) '@langchain/protocol': 0.0.19 '@types/json-schema': 7.0.15 p-queue: 9.3.3 p-retry: 7.1.1 - '@langchain/langgraph@1.4.13(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)))(zod@4.4.3)': + '@langchain/langgraph@1.4.13(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)))(zod@4.5.4)': dependencies: - '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)) - '@langchain/langgraph-checkpoint': 1.1.5(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3))) - '@langchain/langgraph-sdk': 1.10.0(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3))) + '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)) + '@langchain/langgraph-checkpoint': 1.1.5(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4))) + '@langchain/langgraph-sdk': 1.10.0(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4))) '@langchain/protocol': 0.0.18 '@standard-schema/spec': 1.1.0 - zod: 4.4.3 + zod: 4.5.4 transitivePeerDependencies: - react - react-dom - '@langchain/openai@1.5.10(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)))(undici@7.29.0)': + '@langchain/openai@1.5.11(@langchain/core@1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)))(undici@7.29.0)': dependencies: - '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)) + '@langchain/core': 1.2.9(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)) js-tiktoken: 1.0.21 - openai: 7.8.0(undici@7.29.0)(zod@4.4.3) - zod: 4.4.3 + openai: 7.9.0(undici@7.29.0)(zod@4.5.4) + zod: 4.5.4 transitivePeerDependencies: - '@aws-sdk/credential-provider-node' - '@smithy/hash-node' @@ -401,12 +401,12 @@ snapshots: '@workflow/serde@4.1.0': {} - ai@7.0.83(zod@4.4.3): + ai@7.0.90(zod@4.5.4): dependencies: - '@ai-sdk/gateway': 4.0.67(zod@4.4.3) - '@ai-sdk/provider': 4.0.8 - '@ai-sdk/provider-utils': 5.0.32(zod@4.4.3) - zod: 4.4.3 + '@ai-sdk/gateway': 4.0.72(zod@4.5.4) + '@ai-sdk/provider': 4.0.10 + '@ai-sdk/provider-utils': 5.0.36(zod@4.5.4) + zod: 4.5.4 base64-js@1.5.1: {} @@ -424,19 +424,19 @@ snapshots: json-schema@0.4.0: {} - langsmith@0.6.3(@opentelemetry/api@1.9.1)(openai@7.8.0(undici@7.29.0)(zod@4.4.3)): + langsmith@0.6.3(@opentelemetry/api@1.9.1)(openai@7.9.0(undici@7.29.0)(zod@4.5.4)): dependencies: p-queue: 6.6.2 optionalDependencies: '@opentelemetry/api': 1.9.1 - openai: 7.8.0(undici@7.29.0)(zod@4.4.3) + openai: 7.9.0(undici@7.29.0)(zod@4.5.4) mustache@4.2.0: {} - openai@7.8.0(undici@7.29.0)(zod@4.4.3): + openai@7.9.0(undici@7.29.0)(zod@4.5.4): optionalDependencies: undici: 7.29.0 - zod: 4.4.3 + zod: 4.5.4 p-finally@1.0.0: {} @@ -465,4 +465,4 @@ snapshots: undici@7.29.0: {} - zod@4.4.3: {} + zod@4.5.4: {} diff --git a/justfile b/justfile index 894023c00..3b6458401 100644 --- a/justfile +++ b/justfile @@ -19,7 +19,7 @@ build: cargo build # Build the browser wasm package (@everruns/bashkit-wasm) and run its tests. -# Requires: rustup target add wasm32-unknown-unknown; cargo install wasm-bindgen-cli +# Requires: rustup target add wasm32-unknown-unknown; bash scripts/install-wasm-bindgen.sh build-wasm: bash crates/bashkit-wasm/scripts/build.sh release node --test "crates/bashkit-wasm/__test__/*.test.mjs" diff --git a/knowledge/foundations/snapshot-history.md b/knowledge/foundations/snapshot-history.md index 2112d62fd..066f5dbec 100644 --- a/knowledge/foundations/snapshot-history.md +++ b/knowledge/foundations/snapshot-history.md @@ -12,6 +12,13 @@ tags: # Snapshot History and Deltas +## Host callbacks on snapshot construction + +JS `BashTool.fromSnapshot` and `fromSnapshotKeyed` register the caller's supplied +`customBuiltins` after the native interpreter restores state. JS functions are +not serialized: callers reattach them through construction options, and the +registered callbacks then survive `reset()` like ordinary constructor callbacks. + ## Status Implemented. `crates/bashkit/src/snapshot/` holds the object graph, container, diff --git a/knowledge/log.md b/knowledge/log.md index a47b62d5c..2c76758c1 100644 --- a/knowledge/log.md +++ b/knowledge/log.md @@ -1,5 +1,13 @@ # Bashkit Knowledge Update Log +## 2026-09-05 + +* **Adapters**: Deep Agents implements the current structured protocol through native VFS operations; JS snapshot constructors retain supplied callbacks and browser persistence rejects partial traversals. See [Python Package](runtimes/python-package.md), [Snapshot History](foundations/snapshot-history.md), and [Browser Package](runtimes/browser-package.md). +* **Supply chain**: Reviewed dependency deltas without new exemptions; the pinned jiter Git revision now has an explicit audit and its unused Python FFI is banned. Release jobs build immutable validated commits and verify Cargo packages without credentials; see [Dependency Policy](operations/dependencies.md) and [Release Process](operations/release-process.md). + +* **Maintenance**: Defined analyze/fix/ship as the default invocation and verified Monty compatibility pins in [Maintenance](operations/maintenance.md) and [Dependency Policy](operations/dependencies.md). +* **Security**: CI credential-fetch steps exit before examples run with only scoped API keys, the aggregate gate includes WASM jobs, and Anthropic output is capped again after XML escaping. Regression tests cover these boundaries; see [Threat Model](security/threat-model.md). + ## 2026-08-23 * **Security**: TM-DOS-098 tightened. A parked host-call execution stored an unpolled future, so its wall-clock deadline could only fire the next time the host called `next_event()` — a host that took a `HostCallRequest` and never came back retained the whole interpreter. `host_call::spawn_execution` now hands the execution future to a task spawner, so the deadline fires autonomously and the timed-out session is dropped. Dropping the handle aborts the driver, and `into_bash()` no longer recovers a timed-out execution on any target. diff --git a/knowledge/operations/dependencies.md b/knowledge/operations/dependencies.md index 7ba6f6af7..91d538ce1 100644 --- a/knowledge/operations/dependencies.md +++ b/knowledge/operations/dependencies.md @@ -57,6 +57,41 @@ lives there, next to the pin, so it cannot drift away from what it explains. the browser wasm binary. See [Gate rather than reimplement](#gate-rather-than-reimplement). +## Monty compatibility pins + +Monty and monty-types are explicitly constrained to `=0.0.19`. Published +0.0.20, 0.0.21, and 0.0.22 all replace the custom tracker trait with a concrete +tracker and replace per-VM memory accounting with a process-global allocator +probe. Neither is compatible with Bashkit's embeddable security contract: +without the global allocator, aggregate heap growth is uncounted; without +VM checkpoints, synchronous loops cannot consume shared work or observe +cancellation. Moving charges outside `start`/`resume` does not preserve this +contract. This is an enforced compatibility constraint, not an unfinished +maintenance upgrade. See [Python Builtin](../runtimes/python-builtin.md#upgrade-blocker-monty-is-held-at-0019) +for the source evidence and resource-limit regression tests. Keep the Git +`jiter` patch while this compatible Monty release requires the 0.15 line. + +The pinned `jiter` Git package requires a published-base audit plus an exact +Git-delta audit (`audit-as-crates-io = true`). Its local, non-importable full +audit covers the selected `default`/`num-bigint` features. `deny.toml` bans +`jiter/python`: its optional ASCII Python string allocator dereferences +`PyUnicode_New` without checking allocation failure. This code is absent from +the workspace's all-features graph; re-audit before allowing it. Verified the +ban with an isolated manifest selecting the actual pinned Git revision and +`features = ["python"]`: `cargo deny check bans` passed before the restriction +and failed with `feature-banned` after it, while the workspace still passed. +Cargo-deny's [feature restrictions](https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html#the-features-field-optional) +enforce this audit boundary in CI. + +`get-size2` is constrained to `=0.10.1` as a Python-only direct dependency, +so downstream resolution is protected even without this workspace's lockfile. +Both 0.10.2 and 0.10.3 implement `GetSize` for `compact_str` 0.10, whereas +Monty's `ruff_python_ast` 0.0.3 uses 0.9.1; resolving either produces E0277. +The extra edge enables no runtime or feature by itself and adds no crate to +the existing Python graph. Remove it when Monty's AST upgrades its string +type. The Python binding's `num-bigint` dependency must still match Monty's +0.4 line. + ## Measure size and audit surface separately The `idna_adapter` pin removes 21 crates but only **15 KB (0.16%)** from the diff --git a/knowledge/operations/maintenance.md b/knowledge/operations/maintenance.md index ef618def1..4082b8bec 100644 --- a/knowledge/operations/maintenance.md +++ b/knowledge/operations/maintenance.md @@ -18,6 +18,20 @@ Implemented Requirements for pre-release maintenance. Ensures no regressions, stale docs, dependency rot, or security gaps ship in a release. +## Invocation contract + +"Run maintenance", "maintain", and common misspellings such as "maintainace" +and "maintaiance" mean **analyze, fix, and ship**. The request includes local +validation, pushing, PR creation, fixing CI/review findings, and squash-merging +with every required check green. A local commit or a set of deferred issues is +not a completed pass. Only an explicit analysis-only request narrows this outcome. + +The maintain skill and command implement this contract; the ship skill completes +it. Do not stop because a fix is large, audits remain, or a build is slow. Preserve +security contracts and audit criteria. Validate platform-specific behavior in a +suitable environment. Proven upstream incompatibilities may require a tested, +documented safe-version pin; they must not justify weaker execution limits. + ## When to Run - Before every minor or major release @@ -33,8 +47,9 @@ dependency rot, or security gaps ship in a release. 1. Bump version constraint in `Cargo.toml` (workspace or crate-level) 2. Run `cargo build`, fix any compilation errors from API changes 3. Run `cargo test`, fix any test failures - 4. If upgrade requires non-trivial refactoring (>50 lines changed), defer to a - tracked GitHub issue instead of blocking the maintenance pass + 4. Resolve API changes and validate the upgrade. If upstream cannot preserve a + required security contract, document and test the newest safe-version pin; + diff size alone is not grounds for deferral. - `cargo update` run after all version bumps to lock latest patch versions - No known CVEs in dependency tree - License and advisory checks pass (`deny.toml`) @@ -108,8 +123,8 @@ these files with it. - Run `cd .deepsec && pnpm update deepsec@latest` - Run `pnpm deepsec scan --project-id bashkit` - Run `pnpm deepsec process --project-id bashkit --agent codex` - - Review `pnpm deepsec report --project-id bashkit` and create GitHub issues - for any deferred findings + - Review `pnpm deepsec report --project-id bashkit`, fix findings, and verify + regressions before shipping; use the external-blocker policy below only when needed - Security tests exist for every MITIGATED threat - Failpoint tests pass - Unsafe usage reviewed (`cargo geiger`) @@ -263,26 +278,96 @@ See [Coreutils Argument Port](../runtimes/coreutils-args-port.md). #### Escalation Policy Failures persisting **>2 consecutive days** on any workflow (CI, nightly, fuzz) -are blocking: -1. Open GitHub issue with label `ci:nightly` -2. Link failing run(s) -3. Assign to most recent contributor in failing area -4. If upstream dep change: pin to known-good rev, open follow-up issue - -**This section is a hard gate.** The maintenance pass MUST NOT be marked -complete or merged while any of the above checks are red. If the agent cannot -fix a failure, it must open a GitHub issue and report the pass as blocked. - -## Deferred Items - -When a maintenance pass identifies issues too large to fix inline (e.g. -multi-file refactors, cross-cutting changes), the pass must: - -1. Create a GitHub issue for each deferred item with clear scope and reproduction steps -2. Record the issue numbers in the summary below so they are tracked - -Deferred items are **not** failures, they are expected for large-scope -improvements. The requirement is that they are **tracked**, not silently skipped. +are blocking. Inspect failing runs, fix the root cause, and verify recovery in +this pass. Preserve evidence in the PR. If upstream breaks a required contract, +validate and document a known-good pin. Only a genuine external blocker or an +explicitly requested scope split warrants a follow-up issue; include the failing +runs and concrete reason work cannot proceed. + +**This section is a hard gate.** Never mark maintenance complete or merge while +required checks are red. Resolve ordinary failures rather than deferring them. + +## September 2026 pass + +The 2026-09-05 pass starts at `ab04bca2` with main CI and seven daily nightly/fuzz +runs green. It refreshes Rust/npm dependencies and updates DeepSec to 2.3.9. +Monty 0.0.19 and get-size2 0.10.1 are exact compatibility constraints, not an +unimplemented upgrade: newer Monty releases remove the required per-VM tracker, +and newer get-size2 releases conflict with the pinned Ruff AST. Isolated +compatibility builds and 284 Python-feature tests verify the safe versions; +see [Dependency Policy](dependencies.md) and [Python Builtin](../runtimes/python-builtin.md). + +DeepSec analyzed 44 files after 54 matcher hits and reported six findings. +Fixes cover CI credentials, the aggregate WASM gate, Anthropic escaped-output +expansion, and Deep Agents truncation metadata/grep glob filters. Credential-fetch +steps now exit before repository execution; only scoped API keys cross the step +boundary. Tests execute the actual workflow scripts and check process environments. +Deep Agents now implements the current structured protocol through native VFS +operations, with a real `create_deep_agent` integration using a deterministic model. +A matcher scan is not a complete Rust security audit; DeepSec reported low Rust coverage. + +Local verification includes 5,564 workspace unit/integration tests, 167 rustdoc +tests, 17 failpoint tests, 29 security property tests, 77 repository-script tests, +831 Python tests (four Linux-only skips on macOS), 573 JS tests, and 63 browser +WASM tests. The JS/Python native security suites use release builds, matching +published artifacts and CI; debug native stack-stress builds are not interchangeable +with that validation profile. Strict Linux/GNU Bash parity passes all 1,859 cases. +The macOS strict comparison exposes BSD utility/path differences and is not the +GNU compatibility baseline. + +The WASM bindgen installer derives its exact schema version from Cargo.lock, +replaces stale cached CLIs, and verifies the executable on PATH in CI and +publication. Regression tests cover cache mismatch and ambiguous lock versions. + +All-feature clippy/rustdoc, site build and generated-page checks, locked fuzz +compilation/advisory checks, and standalone-workspace dependency/advisory checks +pass. Coreutils regeneration produces no drift. CLI sort/jq/SQLite smoke tests +pass, as do embedded Python/TypeScript external callbacks. The published 0.17.1 +browser example executes `jq` and the bundled child-shell script successfully. + +Unsafe-code review covers the changed dependency deltas and existing native +FFI/callback ownership boundaries; this pass adds no Rust unsafe code. Geiger +reports 449 packages but cannot parse two upstream files (signal-hook-registry +and an aws-lc benchmark helper), and generated includes require source review. +Its successful exit is not proof of complete unsafe coverage. + +The earlier August DeepSec tracking findings are also reproduced and fixed: +browser persistence retains the previous complete snapshot after traversal errors; +BashTool snapshot constructors register supplied custom builtins; SQLite setup +installs only when absent; binary release jobs validate and build one immutable +commit; Cargo publication verifies without registry credentials; CI grants no +write permission or persisted checkout credential; release examples install the +reviewed lockfile rather than resolving fresh dependencies. Regression tests cover +failed/successful browser saves, keyed/unkeyed callbacks, SQLite present/missing +branches, moved release tags, compiler environments, and release package links. + +Supply-chain validation passes with 57 individually reviewed dependency deltas +and a non-importable jiter Rust-only baseline audit. The patched Git revision is +explicitly audited, and `deny.toml` bans jiter's unused Python FFI feature after +review found an unchecked allocation failure there. A negative fixture proves +the feature ban rejects that configuration. Existing exemptions and publisher +trust were not broadened. + +Performance baselines are saved in the respective Criterion and comparison +result directories, and the site timeline is regenerated. The comparison harness +now selects Bash from `PATH`, requires Bash >=4, and records the executable/version; +it previously forced macOS Bash 3.2 and produced invalid oracle errors. The GNU +Bash 5.3.15 run matches all 96 cases with zero errors. Parallel and SQLite +Criterion runs complete successfully. These local measurements are observational, +not a controlled before/after dependency performance claim. + +The aggregate CI gate must depend on every validation job, including `wasm`, +`wasm-component`, and `wasm-web`. The maintenance security tests execute its +condition with each dependency failing, preventing a green gate from hiding +an omitted platform failure. + +## Findings and external blockers + +Keep findings in the active maintenance pass and resolve them before shipping. +Do not create follow-up issues as a substitute for authorized fixes, audits, or +checks. A genuine external blocker or a user-requested scope split may be tracked +explicitly; report the evidence and continue independent work. Never mark a pass +complete or merge while required CI is red. ### Deferred items diff --git a/knowledge/operations/performance-results.md b/knowledge/operations/performance-results.md index 300f398e0..9feb45e2c 100644 --- a/knowledge/operations/performance-results.md +++ b/knowledge/operations/performance-results.md @@ -43,6 +43,15 @@ are the aggregation input for benchmark and eval summaries. Default benchmark recipes that represent a real run MUST save artifacts in the directories above: `just bench`, `just bench-parallel`, `just bench-sqlite`. +The comparison harness resolves `bash` from `PATH` and requires Bash 4 or newer +(case conversion and associative arrays are benchmarked). A missing or older +requested Bash oracle aborts the run instead of publishing misleading output +mismatches; macOS `/bin/bash` 3.2 is unsupported, so put an installed modern Bash +first on `PATH`. JSON reports include optional `runner_versions` metadata and +Markdown records the selected Bash path and version. The site transformer ignores +this additive field; historical reports remain readable. Regression tests in +`crates/bashkit-bench/src/runners.rs` cover PATH precedence and missing/old Bash. + `bashkit-eval` runs through the `mira` host (`just eval`, `just eval-scripting`); mira writes its own run folder under `./results//` and is not part of the benchmark save contract above. @@ -52,7 +61,8 @@ clear that they do not update the site. After a successful saved run, the recipe MUST refresh generated site data: `pnpm --dir site run data:performance` (updates local `/benches` without a full -site build). +site build). The equivalent dependency-free command is +`node site/scripts/build-performance-data.mjs`. ## Site Data Build diff --git a/knowledge/operations/release-process.md b/knowledge/operations/release-process.md index 4cbcc38c2..c36cba329 100644 --- a/knowledge/operations/release-process.md +++ b/knowledge/operations/release-process.md @@ -97,6 +97,17 @@ silently failed. version. If one fails, open a hotfix PR rather than leaving the release half-shipped. +### Release example dependency boundary + +The npm release tests install `examples/pnpm-lock.yaml` with +`pnpm install --frozen-lockfile --ignore-scripts`, then replace only the generated +`@everruns/bashkit` link with the downloaded release artifact. Both native and +Docker test jobs use this graph. Fresh `pnpm add` resolution during publication +would bypass the dependency review and security overrides used by CI, and must +not be used. Scoped API keys are fetched only after installation; the credential +fetch process exits before examples execute. See the +[Threat Model](../security/threat-model.md). + ### CI Automation - On merge to main, `release.yml` detects the `chore(release): prepare vX.Y.Z` commit, extracts notes from CHANGELOG.md, creates the GitHub Release + tag. @@ -207,7 +218,15 @@ to the Release, and pushes a Homebrew formula to `everruns/homebrew-tap` ### publish.yml Trigger: Release published. Publishes to crates.io in dependency order, then -verifies published versions. Secret: `CARGO_REGISTRY_TOKEN`. +verifies published versions. Each crate first passes `cargo publish --dry-run` +without registry credentials. The following `cargo publish --no-verify` upload +receives the step-scoped `CARGO_REGISTRY_TOKEN`, so dependency compilation never +inherits that credential. The CLI verification runs after the core is published. + +CLI and C ABI release validation resolve the fetched tag once, validate the +manifest at that immutable commit, and pass its SHA to build checkouts. The tag +remains the release upload destination, never the build source after validation. +Regression coverage: `scripts/tests/test_release_security.py`. ### publish-python.yml diff --git a/knowledge/runtimes/browser-package.md b/knowledge/runtimes/browser-package.md index 6eca11ba2..80b554c78 100644 --- a/knowledge/runtimes/browser-package.md +++ b/knowledge/runtimes/browser-package.md @@ -20,6 +20,17 @@ tags: > wasm", not "any wasm runtime". The spec filename stays `browser-package.md` > for continuity; the browser is still the primary target. +## Bindgen CLI compatibility + +The CLI schema version must exactly match `wasm-bindgen` in `Cargo.lock`. +`scripts/install-wasm-bindgen.sh` reads that version, reuses a matching installed +binary, forcibly replaces an older cached CLI, and checks the executable on +`PATH` after installation. CI and npm publication invoke this same installer; +there is no separate workflow version pin to drift. It requires Python 3.11+ +(`tomllib`), available on the Ubuntu runners. Missing/ambiguous lock entries or +a shadowing mismatched executable fail before bundle generation. Regression +coverage: `scripts/tests/test_wasm_bindgen_version.py`. + ## Status Implemented (reduced feature set). Local build + headless smoke test green. @@ -172,7 +183,7 @@ core already gates off under `cfg(target_family = "wasm")` (see ```bash rustup target add wasm32-unknown-unknown -cargo install wasm-bindgen-cli +bash scripts/install-wasm-bindgen.sh sudo ./scripts/install-binaryen.sh # optional, -Oz pass bash crates/bashkit-wasm/scripts/build.sh # -> pkg/ node --test "crates/bashkit-wasm/__test__/*.test.mjs" # verify @@ -204,7 +215,10 @@ published, builds `pkg/`, runs the smoke test, and `npm publish`es `@everruns/bashkit-wasm` with provenance (`NPM_TOKEN`, `id-token: write`), same pattern as `publish-js.yml`. Browser example smoke testing writes a file under `/home/user`, reloads the page, and verifies `browserLocal` restores it from -`localStorage`. +`localStorage`. The text-only example adapter commits only a complete traversal; +unreadable files, non-UTF-8 bytes, or directory errors return `false` and leave +the previous snapshot intact. A root confirmed absent through `exists()` saves +an empty snapshot, so deleting the root still clears persisted files. ## Limitations (see [Known Limitations](../operations/limitations.md)) diff --git a/knowledge/runtimes/python-builtin.md b/knowledge/runtimes/python-builtin.md index 21b5cc68a..fa2e6be2d 100644 --- a/knowledge/runtimes/python-builtin.md +++ b/knowledge/runtimes/python-builtin.md @@ -86,9 +86,10 @@ release / 35 debug) against stack overflow from deeply nested expressions. `monty` and `monty-types` are pinned to 0.0.19 and ignored in `.github/dependabot.yml`. This is a security hold, not API-churn convenience. -Monty 0.0.21 changed how `max_memory` is enforced: +Published Monty 0.0.20, 0.0.21, and 0.0.22 all change how `max_memory` is +enforced (verified against each release's `monty-types/src/resource.rs`): -| | 0.0.19 | 0.0.21 | +| | 0.0.19 | 0.0.20–0.0.22 | |---|---|---| | Enforcement | `LimitedTracker::on_grow` accounts VM heap growth in-process | `probe_memory()` reads the `LIVE_MEMORY` / `BASELINE_MEMORY` statics | | Who populates it | the tracker itself | only `monty-alloc`, installed as the **process-wide global allocator** | @@ -117,17 +118,18 @@ standing guard for this, they need no new test to be added: The duration, recursion, and print-collect-cap limits are unaffected and still pass, which localises the regression to allocator-backed memory. -Two further changes land in the same bump and must be handled when the hold is -lifted; neither is a blocker on its own: - -- `ResourceTracker` became a concrete struct (upstream pydantic/monty#613), so - the host can no longer wrap VM checkpoints. `BudgetTracker`'s bridge into the - shared `ExecutionBudget` has to move outside the VM, charging work around - each synchronous `start`/`resume` section instead of inside it. The - per-entry admission reservation that enforces TM-DOS-096 is independent of - the tracker and survives either way. -- `ResourceLimits::new()` is replaced by `Default`, and its builders take plain - values rather than `Option`s. +There is a second security blocker: `ResourceTracker` becomes a concrete +struct in all three releases, removing Bashkit's VM checkpoint hooks. +`BudgetTracker` checks request cancellation/deadlines during execution and +charges shared work on VM/allocation checkpoints. Charging only around a +synchronous `start`/`resume` call cannot interrupt or meter an uninterrupted +Python loop, so it is not a safe replacement. The per-entry admission +reservation enforcing TM-DOS-096 remains necessary but is not a substitute. +An upgrade requires both per-VM memory enforcement without owning the host's +global allocator and equivalent in-VM budget/cancellation checkpoints. + +The API-only changes (`ResourceLimits::new()` becomes `Default`, builders +take plain values instead of `Option`s) are not security blockers. Lifting the hold also **removes** a `[patch.crates-io]` git pin: the patch in the root `Cargo.toml` exists only because `monty 0.0.19` requires diff --git a/knowledge/runtimes/python-package.md b/knowledge/runtimes/python-package.md index 95e5b0932..56d3ecbbc 100644 --- a/knowledge/runtimes/python-package.md +++ b/knowledge/runtimes/python-package.md @@ -237,6 +237,35 @@ invocation regardless of mechanism. `bashkit[langchain]`, `bashkit[deepagents]`, `bashkit[pydantic-ai]`, `bashkit[dev]` (pytest, pytest-asyncio). +## Deep Agents backend + +The optional `deepagents` extra requires Deep Agents 0.7.13 or later and Python +3.11+ (the upstream framework minimum); the base package remains Python 3.9+. +CI installs the extra on Python 3.11–3.14 so protocol tests cannot silently skip. `BashkitBackend` +implements its structured `read`, `ls`, `glob`, `grep`, `write`, `edit`, and +`delete` results; obsolete `ls_info`, `glob_info`, and `grep_raw` are removed. +`execute` propagates either native stream's truncation flag. Timeout policy +remains configured on construction (`timeout_seconds`); per-call timeout +support is not advertised to the framework. + +File operations use the live native VFS directly, avoiding shell output caps, +newline insertion, option injection, and colon-delimited path parsing. They +never access the host filesystem. Transfers preserve arbitrary bytes; writes +refuse existing files, edits report replacement counts, and uploads replace +files. Relative paths resolve against the shared shell's current directory; +discovery always returns absolute paths. Reads return raw selected lines with +pagination metadata, leaving line-number formatting to the framework. + +Grep uses literal substring matching and the framework's shared include-glob +matcher: basename globs at any depth, rooted/path globs relative to the search +root, recursive `**`, braces, and character classes. Traversal patterns are +errors. A total `max_count` reports truncation only when a match is omitted. +Recursive discovery skips symlink entries to avoid cyclic walks. Async methods +use protocol thread dispatch, sharing the same VFS with Bashkit middleware. +`test_deepagents_backend.py` exercises the installed framework protocol and +native extension, including stream caps, exact transfers, malicious names, +search filters, and shared async state. + ## CI `.github/workflows/python.yml`, on push to main and PRs (path-filtered). diff --git a/knowledge/security/threat-model.md b/knowledge/security/threat-model.md index 1487f85c7..8b7b57835 100644 --- a/knowledge/security/threat-model.md +++ b/knowledge/security/threat-model.md @@ -431,14 +431,23 @@ execute permission (mode & 0o111); exit 127 missing / 126 non-executable; sheban | TM-INF-023 | jq `halt` / `halt_error` terminate the host process | jaq-std 3.0's `halt(N)` native calls `std::process::exit()`; untrusted jq filters could exit the entire embedding process, escaping the `ExecResult` sandbox boundary (process-wide DoS) | Strip the upstream `halt` native from `jaq_std::funs::()` and chain in a safe replacement returning `Error::str("halt is disabled in the bashkit sandbox")`; wrapper defs (`halt`, `halt_error`) still resolve, so callers see a normal jq runtime error (exit 5). Regression tests in `builtins::jq::tests`: `halt_does_not_terminate_host_process`, `halt_with_arg_does_not_terminate_host_process`, `halt_error_does_not_terminate_host_process` | **FIXED** | | TM-INF-024 | Host env side-channel via clap `Arg::env(...)` | uutils-ported clap builtins (currently `ls`: `.env("TABSIZE")`/`.env("TIME_STYLE")`) resolved defaults from `std::env`, not `ctx.env`, (1) presence-probe of host env vars via `ls` behaviour; (2) a host-set `TIME_STYLE` became a value source for an unimplemented option, tripping the unsupported-option gate on every plain `ls` for unrelated tenants | Four-layer fix: (1) codegen, `bashkit-coreutils-port` strips runtime `.env(...)` calls and emits a sidecar `_ENV_DEFAULTS: &[EnvDefault]` table per stripped annotation; (2) virtual-env shim, `crate::builtins::clap_env::apply_env_defaults` rewrites argv from `ctx.env` (never `std::env`) before `try_get_matches_from`, emulating clap's "argv > env > default" precedence; (3) static guards, `no_clap_env_in_generated_parsers` forbids runtime `.env(` in `generated/*.rs`; `every_generated_parser_emits_env_defaults_table` enforces the sidecar on every util; (4) defence-in-depth, workspace `clap` drops the `env` cargo feature, so a re-introduced `.env(...)` fails to compile. Coverage: `ls_ignores_host_time_style_and_tabsize` (host env ignored) and `ls_honors_virtual_env_time_style` (virtual env honoured) | **FIXED** | | TM-INF-025 | Untrusted generated Rust runs in drift CI with repository write token | The coreutils argument-drift workflow regenerates Rust from third-party `uutils/coreutils` `uu_app()` then builds/tests bashkit; if the generator preserved arbitrary upstream statements and the job held `contents: write`/`pull-requests: write`, malicious upstream code could execute with repository write impact | Two-layer fix: (1) `bashkit-coreutils-port` validates args-mode `uu_app()` before emission, accepts only a single tail clap `Command` builder chain, or `let = Command::new(...); .(...)` with the tail chained off the let-bound ident; both shapes pass a disallowed-method/closure/macro/block visitor. Regression tests `rejects_executable_statements_in_uu_app`, `rejects_let_with_non_command_initializer`, `rejects_tail_not_chained_off_let_binding`, `rejects_three_statement_body`. (2) `.github/workflows/coreutils-args-drift.yml` separates privilege: regeneration/build/test runs with `contents: read` + `persist-credentials: false`; the `open-pr` job has write permission but never builds or executes generated Rust, only commits the tested artifact | **FIXED** | -| TM-INF-026 | Fork-PR secret exfiltration via approved CI run | GitHub's first-time-contributor gate runs the workflow **from the PR head**, and approved contributors auto-run thereafter. The `examples` job in `ci.yml` set `DOPPLER_TOKEN` at job level and `ANTHROPIC_API_KEY` at step level, so a fork PR could edit any `examples/*.rs`/`build.rs`/script the job runs and exfiltrate. `DOPPLER_TOKEN` is the master key, it fetches every other secret in the Doppler config, so one leak compromises all. Every `doppler run --` also injected the entire config into the step env | Three-layer fix in `.github/workflows/ci.yml`, `js.yml`, `publish-js.yml`: (1) fork guard, secret-using steps gate on `github.event.pull_request.head.repo.fork != true`; (2) step-scoped secrets, no job-level `env: DOPPLER_TOKEN`, each step sets only what it needs; (3) least-privilege injection, every `doppler run` uses `--only-secrets `. Also migrated `ANTHROPIC_API_KEY` onto Doppler so `DOPPLER_TOKEN` is the only secret CI needs from GitHub | **FIXED** | -| TM-INF-027 | Package publish from unverified release refs | A user with Actions/write privileges could dispatch release/publish workflows from a branch or unprotected `vX.Y.Z` tag; `release.yml` dispatches downstream workflows with `--ref "$TAG"`, and `publish-js.yml` treated stable `package.json` semver as `latest` even on non-tag refs, an attacker-controlled ref could source official crates.io/npm/PyPI/CLI/Homebrew artifacts | Release dispatches require full ref `refs/heads/main`; `release.yml` verifies the source is reachable from `origin/main` and the created tag resolves to the current `GITHUB_SHA` before dispatching; `publish.yml` rejects manual runs unless `GITHUB_REF` is a stable release tag matching `Cargo.toml` on a main-reachable commit; `publish-js.yml` requires main-reachable commit, assigns npm `latest` only for stable `vX.Y.Z` tag refs matching `package.json`; `publish-python.yml` applies the same main-reachable stable-tag + Cargo version gates; `cli-binaries.yml` accepts only stable `vX.Y.Z` inputs and verifies workflow ref = tag = `Cargo.toml` version on a main-reachable commit before building/uploading or updating Homebrew | **FIXED** | +| TM-INF-026 | Fork-PR secret exfiltration via approved CI run | GitHub's first-time-contributor gate runs the workflow **from the PR head**, and approved contributors auto-run thereafter. The `examples` job in `ci.yml` set `DOPPLER_TOKEN` at job level and `ANTHROPIC_API_KEY` at step level, so a fork PR could edit any `examples/*.rs`/`build.rs`/script the job runs and exfiltrate. `DOPPLER_TOKEN` is the master key, it fetches every other secret in the Doppler config, so one leak compromises all. Every `doppler run --` also injected the entire config into the step env | Trusted-run gates exclude PR execution from secret-backed examples. Dedicated credential-fetch steps receive `DOPPLER_TOKEN`, request one API key, mask it, and exit before build/example processes run. Execution steps receive only the requested API key through step output/env; Docker inherits it by name, without putting its value in argv. No broad token remains in the execution shell or a live Doppler parent. This is process-lifetime isolation on a trusted runner, not a sandbox against compromised dependencies persisting across steps. Regression tests execute the workflow scripts and inspect child/parent environments: `scripts/tests/test_maintenance_security.py`. CI has contents-read permission only and checkout credentials are not persisted. Release examples install the committed dependency lockfile with lifecycle scripts disabled before linking the built artifact; `scripts/tests/test_ci_supply_chain.py` enforces both boundaries | **FIXED** | +| TM-INF-027 | Package publish from unverified release refs | A user with Actions/write privileges could dispatch release/publish workflows from a branch or unprotected `vX.Y.Z` tag; `release.yml` dispatches downstream workflows with `--ref "$TAG"`, and `publish-js.yml` treated stable `package.json` semver as `latest` even on non-tag refs, an attacker-controlled ref could source official crates.io/npm/PyPI/CLI/Homebrew artifacts | Release dispatches require full ref `refs/heads/main`; `release.yml` verifies the source is reachable from `origin/main` and the created tag resolves to the current `GITHUB_SHA` before dispatching; `publish.yml` rejects manual runs unless `GITHUB_REF` is a stable release tag matching `Cargo.toml` on a main-reachable commit; `publish-js.yml` requires main-reachable commit, assigns npm `latest` only for stable `vX.Y.Z` tag refs matching `package.json`; `publish-python.yml` applies the same main-reachable stable-tag + Cargo version gates; `cli-binaries.yml` accepts only stable `vX.Y.Z` inputs and verifies workflow ref = tag = `Cargo.toml` version on a main-reachable commit before building/uploading or updating Homebrew; CLI and C ABI validation resolve the fetched tag to an immutable commit, read its checked-out manifest, and export that SHA to all build jobs, preventing tag movement from substituting unvalidated source. Real Git tag-movement regressions: `scripts/tests/test_release_security.py` | **FIXED** | | TM-INF-028 | JS callback errors expose host stack traces | When a caller-supplied output or custom-builtin callback throws, propagating `error.stack` or an unsanitized message can expose host file paths and internal function names across the sandbox boundary | Native and wasm bindings propagate `error.message` only, strip absolute-path and `file://` segments, and cap diagnostics at 256 characters. Regressions: `onOutput error does not leak stack trace or host paths` in `crates/bashkit-js/__test__/streaming-output.spec.ts` and `callback errors do not leak host paths or stacks` in `crates/bashkit-wasm/__test__/bashkit-wasm.test.mjs`; mitigations annotated `THREAT[TM-INF-028]` in both binding implementations | **FIXED** | | TM-INF-030 | Raw callback errors leak host internals through ScriptedTool/ToolImpl/ToolRegistry | Tool callbacks may throw errors containing API keys, connection strings, or stack traces; output is attacker-influenced and agent-visible | `sanitize_errors` defaults to true on `ScriptedTool`, `ToolImpl`, and the cross-runtime `ToolRegistry`; every shell/Python/TypeScript registry surface returns the same generic message (`scripted_tool/mod.rs`, `tool_def.rs`, `tool_registry.rs`) | **MITIGATED** | | TM-INF-031 | `final_env` capture bypasses output filtering and size caps | When `capture_final_env` is enabled, env contents are a user-visible output channel that could leak internal markers or exceed output limits | Visibility filtering + output-byte cap applied when building `final_env` (`interpreter/mod.rs`) | **MITIGATED** | | TM-INF-032 | Competitor fixture import executes untrusted upstream code in CI | Automatically downloading or mechanically copying third-party regression scripts into a host-shell oracle would let a compromised upstream run arbitrary commands with CI permissions; a source commit hash proves provenance, not trust | Competitor corpora are manually reviewed, data-only JSON checked into the repository with full commit provenance; CI performs no fetch; Bashkit cases run in the VFS with normal resource limits; only cases explicitly marked `real_bash` run under the host oracle, with a cleared environment and temporary cwd. The schema/classification gate is `competitor_corpus_has_complete_provenance_and_classification`; process contract is [Testing Strategy](../operations/testing.md) | **MITIGATED** | | TM-INF-033 | `time` becomes a high-resolution host timing oracle or exposes host-process metrics | Untrusted scripts repeatedly measure small operations or request GNU CPU/RSS fields to infer co-tenant and host behavior | Monotonic elapsed time follows Bashkit's portable/virtual clock; the Hardened profile floors elapsed values into 100 ms buckets; CPU, RSS, and other host-process fields deterministically report `unavailable`; verbose output includes only Bashkit-owned counters | **MITIGATED** | +**Release compiler credentials**: `publish.yml` verifies each crate using +`cargo publish --dry-run` without registry credentials, then runs a separate +`cargo publish --no-verify` upload with the step-scoped registry token. Dependency +build scripts and proc macros therefore do not inherit the publish token during +verification. This separates process environments; it does not isolate later +steps from a compromised compiler dependency persisting on the same runner. +Workflow execution regressions inspect verification/upload environments in +`scripts/tests/test_release_security.py`. + **TM-INF-022**: Generalizes TM-INF-016 to the whole builtin surface. Originating bug: `builtins/jq/` formatted jaq compile/parse errors with `{:?}`, leaking the jaq `File` struct, the prepended compat-defs source, and raw `Undefined::Filter(N)` tags. Fixed via @@ -596,7 +605,7 @@ Bash::builder() | TM-INJ-019 | `unset` removes readonly variables | `readonly X=v; unset X` removes the variable despite readonly attribute | `execute_unset_builtin` and `Unset` builtin both consult the `VarAttrs::READONLY` flag in the dedicated `var_attrs` map, emit `bash: unset: : cannot unset: readonly variable`, and return exit 1 | **MITIGATED** | | TM-INJ-020 | `declare` overwrites readonly variables | `readonly X=v; declare X=new` overwrites without error | `declare` assignment path consults `VarAttrs::READONLY` via `is_var_readonly()`, emits `bash: declare: : readonly variable`, returns exit 1 | **MITIGATED** | | TM-INJ-021 | `export` overwrites readonly variables | `readonly X=v; export X=new` overwrites without error | `export NAME=VALUE` consults `VarAttrs::READONLY` via `ShellRef::is_var_readonly()`, emits `bash: export: : readonly variable`, returns exit 1 | **MITIGATED** | -| TM-INJ-022 | XML boundary break via tool output (`sanitizeOutput`) | When `sanitizeOutput` is enabled the JS adapters (anthropic, openai) wrap tool output in `` markers. A script that emits `` in its stdout can close the marker early and inject arbitrary text into the LLM context, bypassing the boundary | Escape `&`, `<`, `>` in content before inserting between tags (`anthropic.ts`, `openai.ts` `formatOutput`). Tests: `ai-adapters.spec.ts`, "sanitizeOutput escapes in stdout" and "sanitizeOutput escapes & < > in stdout" for both adapters | **FIXED** | +| TM-INJ-022 | XML boundary break via tool output (`sanitizeOutput`) | When `sanitizeOutput` is enabled the JS adapters (anthropic, openai) wrap tool output in `` markers. A script that emits `` in its stdout can close the marker early and inject arbitrary text into the LLM context, bypassing the boundary | Escape `&`, `<`, `>` in content before inserting between tags (`anthropic.ts`, `openai.ts` `formatOutput`). Both adapters re-cap expanded content without splitting XML entities; wrapper and truncation marker are additional framing. Tests: `ai-adapters.spec.ts`, "sanitizeOutput escapes in stdout" and "sanitizeOutput escapes & < > in stdout" for both adapters | **FIXED** | | TM-INJ-023 | Template injection via `#each` data values | `template` builtin: data values containing template markers (`{{`, `#each`) could be re-expanded as directives when interpolated | Template markers escaped in data values before interpolation (`builtins/template.rs`) | **MITIGATED** | | TM-INJ-024 | Tool schema `$ref` bypass | A ToolDef uses local references for input constraints; direct-keyword-only validation accepts attacker-controlled parameters and invokes the host callback | `ToolRegistry` resolves local JSON Pointer references against the root schema before policy/callback execution and fails closed on invalid, external, unresolved, or cyclic references; `registry_resolves_local_schema_references_before_callback` proves rejected input never reaches the callback | **MITIGATED** | diff --git a/scripts/install-wasm-bindgen.sh b/scripts/install-wasm-bindgen.sh new file mode 100644 index 000000000..cc8b992e8 --- /dev/null +++ b/scripts/install-wasm-bindgen.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# The bindgen CLI schema must exactly match the crate in this checkout's lock. +# Reuse matching cached binaries; replace stale ones and verify the executable on PATH. +set -euo pipefail + +REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +BINDGEN_VERSION=$(python3 - "$REPO_ROOT/Cargo.lock" <<'PY' +import sys +import tomllib + +with open(sys.argv[1], 'rb') as source: + packages = tomllib.load(source)['package'] +versions = {p['version'] for p in packages if p['name'] == 'wasm-bindgen'} +if len(versions) != 1: + sys.exit('Expected exactly one wasm-bindgen version in Cargo.lock') +print(versions.pop()) +PY +) + +EXPECTED="wasm-bindgen $BINDGEN_VERSION" +if [[ "$(wasm-bindgen --version 2>/dev/null || true)" != "$EXPECTED" ]]; then + cargo install wasm-bindgen-cli --version "$BINDGEN_VERSION" --locked --force +fi +if [[ "$(wasm-bindgen --version)" != "$EXPECTED" ]]; then + echo "Error: wasm-bindgen on PATH does not match Cargo.lock ($BINDGEN_VERSION)" >&2 + exit 1 +fi +echo "$EXPECTED (matches Cargo.lock)" diff --git a/scripts/tests/test_ci_sqlite.py b/scripts/tests/test_ci_sqlite.py new file mode 100644 index 000000000..a7d8436a8 --- /dev/null +++ b/scripts/tests/test_ci_sqlite.py @@ -0,0 +1,48 @@ +"""Run the checked-in SQLite bootstrap with an isolated executable search path.""" +import os +from pathlib import Path +import subprocess +import sys +import tempfile +import unittest + +import yaml + +ROOT = Path(__file__).resolve().parents[2] + + +class SqliteBootstrapTests(unittest.TestCase): + def test_install_only_when_sqlite_is_missing(self): + jobs = yaml.safe_load((ROOT / '.github/workflows/ci.yml').read_text())['jobs'] + step = next(step for job in jobs.values() for step in job.get('steps', []) + if step.get('name') == 'Install host sqlite3 for differential tests') + for present in (True, False): + with self.subTest(present=present), tempfile.TemporaryDirectory() as directory: + root = Path(directory) + log = root / 'install.log' + sqlite = root / 'sqlite3' + sqlite_script = '#!/bin/sh\nprintf "3.test\\n"\n' + if present: + sqlite.write_text(sqlite_script) + sqlite.chmod(0o755) + which = root / 'which' + which.write_text('#!/bin/sh\ncommand -v "$1"\n') + which.chmod(0o755) + sudo = root / 'sudo' + sudo.write_text(f'#!{sys.executable}\n' + f''' +from pathlib import Path +import sys +with Path({str(log)!r}).open('a') as log: + log.write(' '.join(sys.argv[1:]) + '\\n') +if sys.argv[1:3] == ['apt-get', 'install']: + path = Path({str(sqlite)!r}) + path.write_text({sqlite_script!r}) + path.chmod(0o755) +''') + sudo.chmod(0o755) + result = subprocess.run(['/bin/bash', '-e', '-o', 'pipefail', '-c', step['run']], + env={**os.environ, 'PATH': str(root)}, capture_output=True, text=True) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn('3.test', result.stdout) + installs = log.read_text().splitlines() if log.exists() else [] + self.assertEqual(installs, [] if present else ['apt-get update', 'apt-get install -y sqlite3']) diff --git a/scripts/tests/test_ci_supply_chain.py b/scripts/tests/test_ci_supply_chain.py new file mode 100644 index 000000000..1417b3cfa --- /dev/null +++ b/scripts/tests/test_ci_supply_chain.py @@ -0,0 +1,44 @@ +"""Keep CI credentials read-only and release examples on reviewed dependencies.""" +import os +from pathlib import Path +import subprocess +import tempfile +import unittest + +import yaml + +ROOT = Path(__file__).resolve().parents[2] + + +class CISupplyChainTests(unittest.TestCase): + def test_ci_has_no_write_permission_or_persisted_checkout_token(self): + workflow = yaml.safe_load((ROOT / '.github/workflows/ci.yml').read_text()) + self.assertEqual(workflow['permissions'], {'contents': 'read'}) + for job in workflow['jobs'].values(): + self.assertNotIn('write', job.get('permissions', {}).values()) + for step in job.get('steps', []): + if step.get('uses', '').startswith('actions/checkout@'): + self.assertIs(step.get('with', {}).get('persist-credentials'), False) + + def test_release_examples_install_lockfile_and_use_built_binding(self): + workflow = yaml.safe_load((ROOT / '.github/workflows/publish-js.yml').read_text()) + for job_name in ('test-js-macos-windows', 'test-js-linux'): + job = workflow['jobs'][job_name] + self.assertNotIn('pnpm add', '\n'.join(step.get('run', '') for step in job['steps'])) + step = next(s for s in job['steps'] if s.get('name') == 'Install example dependencies and link local build') + self.assertEqual(step['working-directory'], 'examples') + for platform in ('Linux', 'Windows'): + with self.subTest(job=job_name, platform=platform), tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root / 'examples').mkdir() + (root / 'crates/bashkit-js').mkdir(parents=True) + (root / 'crates/bashkit-js/index.js').write_text('local release artifact') + (root / 'bin').mkdir() + pnpm = root / 'bin/pnpm' + pnpm.write_text('#!/bin/sh\n[ "$*" = "install --frozen-lockfile --ignore-scripts" ] || exit 42\nmkdir -p node_modules/@everruns/bashkit\nprintf stale > node_modules/@everruns/bashkit/index.js\n') + pnpm.chmod(0o755) + script = step['run'].replace('${{ github.workspace }}', str(root)) + env = {**os.environ, 'RUNNER_OS': platform, 'PATH': str(root / 'bin') + os.pathsep + os.environ['PATH']} + result = subprocess.run(['/bin/bash', '-e', '-o', 'pipefail', '-c', script], cwd=root / 'examples', env=env, capture_output=True, text=True) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual((root / 'examples/node_modules/@everruns/bashkit/index.js').read_text(), 'local release artifact') diff --git a/scripts/tests/test_maintenance_security.py b/scripts/tests/test_maintenance_security.py new file mode 100644 index 000000000..9891ac6e0 --- /dev/null +++ b/scripts/tests/test_maintenance_security.py @@ -0,0 +1,138 @@ +"""Exercise CI gates and the environment passed to secret-using examples.""" +import json +import os +from pathlib import Path +import re +import subprocess +import sys +import tempfile +import unittest + +import yaml + +ROOT = Path(__file__).resolve().parents[2] + + +class MaintenanceSecurityTests(unittest.TestCase): + def test_check_gate_rejects_each_failed_job(self): + jobs = yaml.safe_load((ROOT / '.github/workflows/ci.yml').read_text())['jobs'] + gate = jobs['check'] + success_script = re.sub(r'\$\{\{ needs\.([\w-]+)\.result \}\}', 'success', gate['steps'][0]['run']) + self.assertEqual(subprocess.run(['bash', '-c', success_script], capture_output=True).returncode, 0) + for failed in set(jobs) - {'check'}: + with self.subTest(job=failed): + self.assertIn(failed, gate['needs']) + script = gate['steps'][0]['run'] + script = re.sub(r'\$\{\{ needs\.([\w-]+)\.result \}\}', + lambda m: 'failure' if m[1] == failed else 'success', script) + result = subprocess.run(['bash', '-c', script], capture_output=True) + self.assertNotEqual(result.returncode, 0) + + def test_broad_credential_steps_never_execute_repository_code(self): + for workflow in ('ci.yml', 'js.yml', 'publish-js.yml'): + jobs = yaml.safe_load((ROOT / '.github/workflows' / workflow).read_text())['jobs'] + for job in jobs.values(): + self.assertNotIn('DOPPLER_TOKEN', job.get('env', {})) + for step in job.get('steps', []): + if 'DOPPLER_TOKEN' not in step.get('env', {}): + continue + with self.subTest(workflow=workflow, step=step['name']): + script = step.get('run', '') + self.assertNotRegex(script, r'\b(cargo|node|bun|docker)\b|examples/') + self.assertNotIn('doppler run', script) + self.assertNotIn('uses', step) + + def test_optional_ci_examples_skip_failed_fetches(self): + steps = yaml.safe_load((ROOT / '.github/workflows/ci.yml').read_text())['jobs']['examples']['steps'] + for ident, name in [('anthropic', 'Run LLM agent example'), ('openai', 'Run harness OpenAI joke example')]: + fetch = next(step for step in steps if step.get('id') == ident) + run = next(step for step in steps if step.get('name') == name) + self.assertTrue(fetch['continue-on-error']) + self.assertTrue(run['continue-on-error']) + self.assertIn(f"steps.{ident}.outcome == 'success'", run['if']) + + def test_actual_workflow_scripts_separate_fetch_and_execution(self): + """Execute checked-in scripts; replace executables, not shell wrappers.""" + for workflow in ('ci.yml', 'js.yml', 'publish-js.yml'): + jobs = yaml.safe_load((ROOT / '.github/workflows' / workflow).read_text())['jobs'] + for name, job in jobs.items(): + fetches = [step for step in job.get('steps', []) + if 'doppler secrets get' in step.get('run', '')] + if not fetches: + continue + with self.subTest(workflow=workflow, job=name), tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root / 'bin').mkdir() + log = root / 'calls.jsonl' + stub = root / 'bin' / 'doppler' + stub.write_text(f"#!{sys.executable}\n" + r''' +import json, os, pathlib, sys +name = pathlib.Path(sys.argv[0]).name +broad = 'DOPPLER_TOKEN' in os.environ +parent_env = pathlib.Path('/proc') / str(os.getppid()) / 'environ' +parent_broad = b'DOPPLER_TOKEN=' in parent_env.read_bytes() if parent_env.exists() else False +with open(os.environ['CALL_LOG'], 'a') as log: + log.write(json.dumps({'name': name, 'args': sys.argv[1:], 'broad': broad, + 'parent_broad': parent_broad, 'pid': os.getpid()}) + '\n') +if name == 'doppler': + assert broad + assert sys.argv[1:3] == ['secrets', 'get'] and sys.argv[4:] == ['--plain'] + print('scoped-' + sys.argv[3]) +else: + assert not broad and not parent_broad + for key in ('OPENAI_API_KEY', 'ANTHROPIC_API_KEY'): + if key in os.environ: + assert os.environ[key] == 'scoped-' + key + if name == 'cargo': + assert not any(key in os.environ for key in ('OPENAI_API_KEY', 'ANTHROPIC_API_KEY')) + if name == 'docker': + assert '-e' in sys.argv and 'OPENAI_API_KEY' in sys.argv + assert not any('scoped-' in arg or arg.startswith('OPENAI_API_KEY=') for arg in sys.argv) +''') + stub.chmod(0o755) + for executable in ('node', 'bun', 'deno', 'cargo', 'bash'): + (root / 'bin' / executable).symlink_to(stub) + (root / 'bin' / 'docker').symlink_to(stub) + (root / 'target/debug/examples').mkdir(parents=True) + (root / 'target/debug/examples/agent_tool').symlink_to(stub) + base_env = {key: value for key, value in os.environ.items() + if key not in ('DOPPLER_TOKEN', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY')} + base_env.update(PATH=str(root / 'bin') + os.pathsep + base_env['PATH'], CALL_LOG=str(log)) + outputs = {} + for step in job['steps']: + if step not in fetches and step.get('name') not in ( + 'Run AI examples', 'Run LLM agent example', + 'Run harness OpenAI joke example', 'Build LLM examples without credentials' + ): + continue + output = root / 'output' + output.write_text('') + env = {**base_env, 'GITHUB_OUTPUT': str(output)} + for key, value in step.get('env', {}).items(): + if key == 'DOPPLER_TOKEN': + env[key] = 'synthetic-broad-token' + else: + reference = re.fullmatch(r'\$\{\{ steps\.([\w-]+)\.outputs\.([\w-]+) \}\}', value) + self.assertIsNotNone(reference, value) + env[key] = outputs[reference.groups()] + script = step['run'] + replacements = {'matrix.run': 'node', 'github.workspace': str(root), + 'steps.docker.outputs.PLATFORM': 'linux/amd64', + 'steps.docker.outputs.IMAGE': 'example-image'} + for expression, value in replacements.items(): + script = script.replace('${{ ' + expression + ' }}', value) + self.assertNotIn('${{', script) + result = subprocess.run(['/bin/bash', '-e', '-o', 'pipefail', '-c', script], + env=env, cwd=root, capture_output=True, text=True) + self.assertEqual(result.returncode, 0, result.stderr) + for line in output.read_text().splitlines(): + key, value = line.split('=', 1) + self.assertIn('::add-mask::' + value, result.stdout) + outputs[step['id'], key] = value + calls = [json.loads(line) for line in log.read_text().splitlines()] + self.assertTrue(any(call['name'] == 'doppler' for call in calls)) + self.assertTrue(any(call['name'] != 'doppler' for call in calls)) + for call in calls: + if call['name'] != 'doppler': + self.assertFalse(call['broad']) + self.assertFalse(call['parent_broad']) diff --git a/scripts/tests/test_release_security.py b/scripts/tests/test_release_security.py new file mode 100644 index 000000000..4ce45b571 --- /dev/null +++ b/scripts/tests/test_release_security.py @@ -0,0 +1,104 @@ +"""Release source identity and compiler credential isolation regressions.""" +import json +import os +from pathlib import Path +import subprocess +import tempfile +import unittest + +import yaml + +ROOT = Path(__file__).resolve().parents[2] + + +def jobs(name): + return yaml.safe_load((ROOT / '.github/workflows' / name).read_text())['jobs'] + + +class ReleaseSecurityTests(unittest.TestCase): + def test_binary_builds_use_validated_immutable_commit(self): + for name in ('c-api-binaries.yml', 'cli-binaries.yml'): + with self.subTest(workflow=name): + workflow = jobs(name) + validator = workflow['validate-tag'] + source = next(s for s in validator['steps'] if s.get('name') == 'Verify tag source') + self.assertEqual(validator['outputs']['sha'], '${{ steps.source.outputs.sha }}') + self.assertEqual(source['id'], 'source') + checkout = next(s for s in workflow['build']['steps'] if s.get('uses', '').startswith('actions/checkout@')) + self.assertEqual(checkout['with']['ref'], '${{ needs.validate-tag.outputs.sha }}') + # Re-fetch can move a tag. Version validation must read that same + # resolved tree, never the earlier checkout's Cargo.toml. + script = source['run'] + self.assertLess(script.index('git fetch'), script.index('git checkout --detach "$TAG_SHA"')) + self.assertLess(script.index('git checkout --detach "$TAG_SHA"'), script.index('CARGO_VERSION=')) + self.assertGreater(script.index('echo "sha=$TAG_SHA"'), script.index('git merge-base')) + + def test_validation_reads_refetched_tree_and_returns_stable_sha(self): + for name in ('c-api-binaries.yml', 'cli-binaries.yml'): + with self.subTest(workflow=name), tempfile.TemporaryDirectory() as directory: + root = Path(directory) + origin = root / 'origin' + checkout = root / 'checkout' + def git(*args, cwd=origin): + return subprocess.check_output(['git', *args], cwd=cwd, stderr=subprocess.DEVNULL, text=True).strip() + origin.mkdir() + git('init', '-b', 'main') + git('config', 'user.name', 'Release test fixture') + git('config', 'user.email', 'fixture@example.invalid') + (origin / 'Cargo.toml').write_text('version = "1.2.3"\n') + git('add', '.') + git('commit', '-m', 'valid release') + valid = git('rev-parse', 'HEAD') + git('tag', 'v1.2.3') + git('clone', str(origin), str(checkout), cwd=root) + script = next(s['run'] for s in jobs(name)['validate-tag']['steps'] if s.get('name') == 'Verify tag source') + output = root / 'outputs' + env = dict(os.environ, RELEASE_TAG='v1.2.3', GITHUB_SHA=valid, GITHUB_OUTPUT=str(output)) + result = subprocess.run(['bash', '-e', '-c', script], cwd=checkout, env=env, capture_output=True, text=True) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn(f'sha={valid}', output.read_text()) + # Remote tag now points at a different version; the old checkout + # still has the valid manifest, so validating it would be a bypass. + (origin / 'Cargo.toml').write_text('version = "9.9.9"\n') + git('commit', '-am', 'different source') + moved = git('rev-parse', 'HEAD') + git('tag', '-f', 'v1.2.3') + env['GITHUB_SHA'] = moved + output.unlink() + result = subprocess.run(['bash', '-e', '-c', script], cwd=checkout, env=env, capture_output=True, text=True) + self.assertNotEqual(result.returncode, 0) + self.assertIn('does not match Cargo.toml', result.stderr) + self.assertFalse(output.exists()) + # The successful validation's exported SHA still builds old source. + git('checkout', '--detach', valid, cwd=checkout) + self.assertEqual((checkout / 'Cargo.toml').read_text(), 'version = "1.2.3"\n') + + def test_verification_compiler_never_inherits_registry_token(self): + for name in ('publish-bashkit', 'publish-bashkit-cli'): + with self.subTest(job=name), tempfile.TemporaryDirectory() as directory: + job = jobs('publish.yml')[name] + steps = [s for s in job['steps'] if 'cargo publish' in s.get('run', '')] + self.assertEqual(len(steps), 2) + self.assertNotIn('CARGO_REGISTRY_TOKEN', job.get('env', {})) + root = Path(directory) + cargo = root / 'cargo' + cargo.write_text('#!/usr/bin/env python3\nimport json,os,sys\nwith open(os.environ["TRACE"], "a") as f: f.write(json.dumps({"args":sys.argv[1:],"token":os.environ.get("CARGO_REGISTRY_TOKEN")})+"\\n")\n') + cargo.chmod(0o755) + trace = root / 'trace' + for step in steps: + env = dict(os.environ, PATH=f'{root}:{os.environ["PATH"]}', TRACE=str(trace)) + env.pop('CARGO_REGISTRY_TOKEN', None) + if 'CARGO_REGISTRY_TOKEN' in step.get('env', {}): + env['CARGO_REGISTRY_TOKEN'] = 'sentinel-registry-secret' + subprocess.run(['bash', '-e', '-c', step['run']], env=env, check=True) + calls = [json.loads(line) for line in trace.read_text().splitlines()] + self.assertIn('--dry-run', calls[0]['args']) + self.assertNotIn('--no-verify', calls[0]['args']) + self.assertIsNone(calls[0]['token']) + self.assertIn('--no-verify', calls[1]['args']) + self.assertNotIn('--dry-run', calls[1]['args']) + self.assertEqual(calls[1]['token'], 'sentinel-registry-secret') + + +if __name__ == '__main__': + unittest.main() diff --git a/scripts/tests/test_wasm_bindgen_version.py b/scripts/tests/test_wasm_bindgen_version.py new file mode 100644 index 000000000..8489f83b4 --- /dev/null +++ b/scripts/tests/test_wasm_bindgen_version.py @@ -0,0 +1,61 @@ +"""Bindgen CLI schema must follow the checked-in Cargo.lock, including caches.""" +import os +from pathlib import Path +import shutil +import subprocess +import tempfile +import unittest + +import yaml + +ROOT = Path(__file__).resolve().parents[2] +INSTALLER = 'scripts/install-wasm-bindgen.sh' + + +class WasmBindgenVersionTests(unittest.TestCase): + def test_ci_and_publish_use_lock_derived_installer(self): + for name in ('ci.yml', 'publish-wasm.yml'): + with self.subTest(workflow=name): + workflow = yaml.safe_load((ROOT / '.github/workflows' / name).read_text()) + self.assertNotIn('WASM_BINDGEN_VERSION', workflow.get('env', {})) + steps = [step for job in workflow['jobs'].values() for step in job.get('steps', []) + if step.get('name') == 'Install wasm-bindgen-cli'] + self.assertEqual(len(steps), 1) + self.assertEqual(steps[0]['run'], f'bash {INSTALLER}') + + def test_installer_replaces_stale_cache_and_reuses_matching_binary(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root / 'scripts').mkdir() + shutil.copy(ROOT / INSTALLER, root / INSTALLER) + (root / 'Cargo.lock').write_text('version = 4\n[[package]]\nname = "wasm-bindgen"\nversion = "0.2.999"\n') + (root / 'bin').mkdir() + state = root / 'version' + state.write_text('0.2.126') + log = root / 'cargo-log' + (root / 'bin/wasm-bindgen').write_text('#!/bin/sh\nprintf "wasm-bindgen %s\\n" "$(cat "$STATE")"\n') + (root / 'bin/cargo').write_text('#!/bin/sh\nprintf "%s\\n" "$*" >> "$LOG"\nprintf 0.2.999 > "$STATE"\n') + for command in (root / 'bin').iterdir(): + command.chmod(0o755) + env = dict(os.environ, PATH=f'{root / "bin"}:{os.environ["PATH"]}', STATE=str(state), LOG=str(log)) + for _ in range(2): + result = subprocess.run(['bash', str(root / INSTALLER)], env=env, capture_output=True, text=True) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(log.read_text().splitlines(), ['install wasm-bindgen-cli --version 0.2.999 --locked --force']) + # A shadowing old binary must fail the post-install check. + state.write_text('0.2.126') + (root / 'bin/cargo').write_text('#!/bin/sh\nexit 0\n') + result = subprocess.run(['bash', str(root / INSTALLER)], env=env, capture_output=True, text=True) + self.assertNotEqual(result.returncode, 0) + self.assertIn('does not match Cargo.lock', result.stderr) + for lock in ('version = 4\npackage = []\n', + '[[package]]\nname = "wasm-bindgen"\nversion = "0.2.1"\n[[package]]\nname = "wasm-bindgen"\nversion = "0.2.2"\n'): + (root / 'Cargo.lock').write_text(lock) + result = subprocess.run(['bash', str(root / INSTALLER)], env=env, capture_output=True, text=True) + self.assertNotEqual(result.returncode, 0) + self.assertIn('exactly one wasm-bindgen version', result.stderr) + + + +if __name__ == '__main__': + unittest.main() diff --git a/site/package.json b/site/package.json index 05b87d4d0..e1a5fb923 100644 --- a/site/package.json +++ b/site/package.json @@ -20,23 +20,23 @@ "verify:docs": "node scripts/verify-doc-routes.mjs && node scripts/verify-inline-svg.mjs" }, "dependencies": { - "@astrojs/markdown-remark": "^7.2.4", - "@astrojs/sitemap": "^3.7.3", - "@everruns/bashkit-wasm": "0.16.0", - "astro": "^7.2.9", + "@astrojs/markdown-remark": "^7.3.0", + "@astrojs/sitemap": "^3.7.4", + "@everruns/bashkit-wasm": "0.17.1", + "astro": "^7.3.1", "rehype-autolink-headings": "^7.1.0", "rehype-slug": "^6.0.0" }, "devDependencies": { "@astrojs/check": "^0.9.10", - "@types/node": "^26.4.0", + "@types/node": "^26.4.1", "typescript": "^7.0.2", - "wrangler": "^4.127.0" + "wrangler": "^4.129.0" }, "pnpm": { "overrides": { "esbuild": ">=0.28.1", - "fast-uri": "^3.1.5", + "fast-uri": "^3.1.7", "js-yaml": "^4.3.1", "nanoid": "^3.3.17", "postcss": ">=8.5.18", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index dae926d76..4b8927937 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -6,7 +6,7 @@ settings: overrides: esbuild: '>=0.28.1' - fast-uri: ^3.1.5 + fast-uri: ^3.1.7 js-yaml: ^4.3.1 nanoid: ^3.3.17 postcss: '>=8.5.18' @@ -21,17 +21,17 @@ importers: .: dependencies: '@astrojs/markdown-remark': - specifier: ^7.2.4 - version: 7.2.4 + specifier: ^7.3.0 + version: 7.3.0 '@astrojs/sitemap': - specifier: ^3.7.3 - version: 3.7.3 + specifier: ^3.7.4 + version: 3.7.4 '@everruns/bashkit-wasm': - specifier: 0.16.0 - version: 0.16.0 + specifier: 0.17.1 + version: 0.17.1 astro: - specifier: ^7.2.9 - version: 7.2.9(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.4.0)(yaml@2.9.0) + specifier: ^7.3.1 + version: 7.3.1(@astrojs/markdown-remark@7.3.0)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.4.1)(yaml@2.9.0) rehype-autolink-headings: specifier: ^7.1.0 version: 7.1.0 @@ -43,14 +43,14 @@ importers: specifier: ^0.9.10 version: 0.9.10(prettier@3.9.6)(typescript@7.0.2) '@types/node': - specifier: ^26.4.0 - version: 26.4.0 + specifier: ^26.4.1 + version: 26.4.1 typescript: specifier: ^7.0.2 version: 7.0.2 wrangler: - specifier: ^4.127.0 - version: 4.127.0(@types/node@26.4.0) + specifier: ^4.129.0 + version: 4.129.0(@types/node@26.4.1) packages: @@ -124,11 +124,11 @@ packages: '@astrojs/compiler@2.13.1': resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==} - '@astrojs/internal-helpers@0.10.4': - resolution: {integrity: sha512-nozZSy/mKYLqe4YrqbKtdOszedAfXYCtw3wZ0d+CAjz4GqQ4L9rl1ltIL5BlgwmYVinJg/RZ0MgGuWOdlyRZlA==} + '@astrojs/internal-helpers@0.11.0': + resolution: {integrity: sha512-3rzxJ+xbo0+8YyqOzLziIN32wmsHdCjEVz2sGOpRxJ+Ben/KiLph4ItxBy1abEL+E8fkRzqjg0rfXmaHJGw9JA==} - '@astrojs/language-server@2.16.14': - resolution: {integrity: sha512-YPXkBu6N4d1sT09pvBmIDGZay+1MemV551FSgdEM3aZRDzbkxd2H7Cvf8MsVJLVB1mIEyTf1XbMN/30gp7s46w==} + '@astrojs/language-server@2.16.16': + resolution: {integrity: sha512-KuS17AOOqH/M5mHXPmKvtp8L+NNteARC7Xjf395+9R9dtJOBndqdStwU4V+OKzYZpgZ+hliV13knzx/oMtFl7Q==} hasBin: true peerDependencies: prettier: ^3.0.0 @@ -139,18 +139,18 @@ packages: prettier-plugin-astro: optional: true - '@astrojs/markdown-remark@7.2.4': - resolution: {integrity: sha512-MvspGMynWKAjTe4/lTUdmBPHIFKNVLTCF6UlyWGogTGzNrTvjD+D4n48k7h8swxsEPKHK2TwxkZO7uoaCv1Pow==} + '@astrojs/markdown-remark@7.3.0': + resolution: {integrity: sha512-mRwn2W2PKkOj8XEAiD3b9RIF4qq6FKB6U4c98GxFLkEJH7uA6ZZv8TiqVjJSPMANLb1NtwBTgkY+KO4mTXnT/Q==} - '@astrojs/markdown-satteri@0.3.8': - resolution: {integrity: sha512-n8ItpFTCmlDsVR5+rwDmehSf+jFCYLWmZiisZNGuF7xILqYhsVeBfcha4qgS2Seq3fAc9Tm58ZVrvqFQ6RQgRQ==} + '@astrojs/markdown-satteri@0.4.0': + resolution: {integrity: sha512-wykOOW9KsUVcZweOpY/CeXpdKcCKZy6fQbdcteWFuI75+sQCiqxYM7VKsGa5b+aGl3cYQscFY37rsbbyal5MRw==} '@astrojs/prism@4.0.2': resolution: {integrity: sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==} engines: {node: '>=22.12.0'} - '@astrojs/sitemap@3.7.3': - resolution: {integrity: sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==} + '@astrojs/sitemap@3.7.4': + resolution: {integrity: sha512-LbKNC24bdUWcQf/pThB6qLlSqHojxGjZDURIzFocY8rlWnAn2t74nnhnK6S5x0NHriHoAduLEpVjRykmeGiVvA==} '@astrojs/telemetry@3.3.3': resolution: {integrity: sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==} @@ -246,32 +246,32 @@ packages: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260826.1': - resolution: {integrity: sha512-8UsGGY8ZUiYHOWdsxBlNsGmaHBGArVwJ3CM4nWpfBhthjjYe4M/OqrTpqKF7NNWb63qQiv1d8Z+Z6/hmUqNKIQ==} + '@cloudflare/workerd-darwin-64@1.20260903.1': + resolution: {integrity: sha512-FG+4mGxAXhKiL/1temH42alevIkumtYXNidhTa//3yULpzux6APw5UNVocI/vCQ1yG1YOEZRfbVy8lyuipM9MQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260826.1': - resolution: {integrity: sha512-0bLqVQYsQ3v3FdYGmzh23vi9fJeYTBx19o4LUySIsRcgBggGSlR39ml162vTXvZzUISOjVW2qfL7Y+SMrrfXmQ==} + '@cloudflare/workerd-darwin-arm64@1.20260903.1': + resolution: {integrity: sha512-o241VefnjG8eG+kGap5CjgV4zOT5UaAmS3OR1VZCpNj7vkXGxvp9KftKvtQgcCsIqJKaKx+7Xd7xq0L1DjkfPQ==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260826.1': - resolution: {integrity: sha512-DTC0yWzybX4gUH5Q1pJo3UwEQjp0Gmz0Q71I+39xT9SXBesX5QndOIQv45yHQ86Z84EzK2WwaENdlpmDSvJKmw==} + '@cloudflare/workerd-linux-64@1.20260903.1': + resolution: {integrity: sha512-/VEvvtQ/XKf6HlBbg6FbvpwcpfYUcx6Fv6RkASY8DyEmUyuJ8rc7Qxil83ClRFoBzz/GY0BV94UZ6F+wers/hg==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260826.1': - resolution: {integrity: sha512-PFerWi+DP2Ckc6eATAS4dhotBt8IeXJjTzIgy18H+uqsswlXc7A8HsnAunHL9v/7/BjCgq46iMo2g4iUAsEpDw==} + '@cloudflare/workerd-linux-arm64@1.20260903.1': + resolution: {integrity: sha512-OWhihGC6KoTXF4u2C1AonfpgXeM4/7p/1IXuALqXESmFUpLLP5gZhRzjSk/gWW+mrCZDfSrvnjifl+lRqselbA==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260826.1': - resolution: {integrity: sha512-X26hulrG2MSSfpRmjZbCq98LNaZnrRqbmGcgo7g1U/U0nJ5npYruPm3fAyZ2y6VDr5CmQo9BLCahxP8VB/QR6A==} + '@cloudflare/workerd-windows-64@1.20260903.1': + resolution: {integrity: sha512-soPMF9/aMHlHKK7M0vq5HrRRicPbnZO1F6ZZ7JWN8EltxWJU5L7CEq7CxjO878DzyPx7gYvqBFy3SVgdZKZjMQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -469,8 +469,8 @@ packages: cpu: [x64] os: [win32] - '@everruns/bashkit-wasm@0.16.0': - resolution: {integrity: sha512-6Lx6tbZwZ9qHUsXDdNdYgXhjQFf8WTnDkJM0wVnIauEQ/u2v4bbl6AVIBZzR+4ndkGlA5njKYP2+j8Esq05OVg==} + '@everruns/bashkit-wasm@0.17.1': + resolution: {integrity: sha512-wAtZR7OOC/EqVik6WOXOiXfP1Zwm41XjNtCVtKQ33X47PpQCuy/qT4prJNcwnWk5cnNQwnXJyiCfND6mnIO1aA==} '@img/colour@1.1.0': resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} @@ -622,15 +622,15 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/sourcemap-codec@1.6.0': resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@mdx-js/mdx@3.1.1': + resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + '@napi-rs/wasm-runtime@1.2.3': resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} @@ -641,8 +641,8 @@ packages: '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} - '@oxc-project/types@0.147.0': - resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==} + '@oxc-project/types@0.148.0': + resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} '@poppinss/colors@4.1.6': resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} @@ -653,92 +653,92 @@ packages: '@poppinss/exception@1.2.3': resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} - '@rolldown/binding-android-arm-eabi@1.2.6': - resolution: {integrity: sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==} + '@rolldown/binding-android-arm-eabi@1.2.7': + resolution: {integrity: sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@rolldown/binding-android-arm64@1.2.6': - resolution: {integrity: sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==} + '@rolldown/binding-android-arm64@1.2.7': + resolution: {integrity: sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.2.6': - resolution: {integrity: sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==} + '@rolldown/binding-darwin-arm64@1.2.7': + resolution: {integrity: sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.2.6': - resolution: {integrity: sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==} + '@rolldown/binding-darwin-x64@1.2.7': + resolution: {integrity: sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.2.6': - resolution: {integrity: sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==} + '@rolldown/binding-freebsd-x64@1.2.7': + resolution: {integrity: sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.2.6': - resolution: {integrity: sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.7': + resolution: {integrity: sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.2.6': - resolution: {integrity: sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==} + '@rolldown/binding-linux-arm64-gnu@1.2.7': + resolution: {integrity: sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.2.6': - resolution: {integrity: sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==} + '@rolldown/binding-linux-arm64-musl@1.2.7': + resolution: {integrity: sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-ppc64-gnu@1.2.6': - resolution: {integrity: sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==} + '@rolldown/binding-linux-ppc64-gnu@1.2.7': + resolution: {integrity: sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - '@rolldown/binding-linux-s390x-gnu@1.2.6': - resolution: {integrity: sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==} + '@rolldown/binding-linux-s390x-gnu@1.2.7': + resolution: {integrity: sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.2.6': - resolution: {integrity: sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==} + '@rolldown/binding-linux-x64-gnu@1.2.7': + resolution: {integrity: sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.2.6': - resolution: {integrity: sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==} + '@rolldown/binding-linux-x64-musl@1.2.7': + resolution: {integrity: sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.2.6': - resolution: {integrity: sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==} + '@rolldown/binding-openharmony-arm64@1.2.7': + resolution: {integrity: sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-win32-arm64-msvc@1.2.6': - resolution: {integrity: sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==} + '@rolldown/binding-win32-arm64-msvc@1.2.7': + resolution: {integrity: sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.2.6': - resolution: {integrity: sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==} + '@rolldown/binding-win32-x64-msvc@1.2.7': + resolution: {integrity: sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -802,6 +802,9 @@ packages: '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mdx@2.0.14': + resolution: {integrity: sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==} + '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -811,12 +814,15 @@ packages: '@types/node@24.13.3': resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} - '@types/node@26.4.0': - resolution: {integrity: sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==} + '@types/node@26.4.1': + resolution: {integrity: sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==} '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -940,8 +946,8 @@ packages: cpu: [x64] os: [win32] - '@ungap/structured-clone@1.3.3': - resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + '@ungap/structured-clone@1.4.0': + resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} '@volar/kit@2.4.28': resolution: {integrity: sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==} @@ -969,6 +975,16 @@ packages: '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -1014,12 +1030,16 @@ packages: array-iterate@2.0.1: resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} - astro@7.2.9: - resolution: {integrity: sha512-o5nZFo/bieF6rp4x9sQSLhI7GO7Bahpld38Y4LvX27nFoxNiuttjI+Hea81w5ksORLHgdPUlQpU9obPz5QRa8g==} + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + astro@7.3.1: + resolution: {integrity: sha512-A/bJYHtc6n0UAdROY9W948fW6lX0pnUA+JWKW+IGCXRyDIGN2MsXC0OlS8onZGJjr+sv8htemwOc9W5PBRXCkw==} engines: {node: '>=22.12.0', npm: '>=9.6.5', pnpm: '>=7.1.0'} hasBin: true peerDependencies: - '@astrojs/markdown-remark': 7.2.4 + '@astrojs/markdown-remark': ^7.3.0 peerDependenciesMeta: '@astrojs/markdown-remark': optional: true @@ -1049,6 +1069,9 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1069,6 +1092,9 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -1186,6 +1212,12 @@ packages: es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + esbuild@0.28.2: resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} engines: {node: '>=18'} @@ -1199,6 +1231,27 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.1: + resolution: {integrity: sha512-B0np3dcdxqILX5e9nEi5/Fr4K7gL4oYFVPV1zRa2e9wRCbQoZZNWOZFYyoInvXUPJXBXjss+QXlWLJChDEHDkA==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} @@ -1214,8 +1267,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.5: - resolution: {integrity: sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==} + fast-uri@3.1.7: + resolution: {integrity: sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==} fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} @@ -1285,9 +1338,15 @@ packages: hast-util-raw@9.1.0: resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + hast-util-to-html@9.0.5: resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + hast-util-to-parse5@8.0.1: resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} @@ -1312,14 +1371,29 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-docker@4.0.0: resolution: {integrity: sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==} engines: {node: '>=20'} hasBin: true + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -1424,6 +1498,10 @@ packages: magicast@0.5.4: resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==} + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -1454,6 +1532,18 @@ packages: mdast-util-gfm@3.1.0: resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} @@ -1496,12 +1586,30 @@ packages: micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + micromark-factory-destination@2.0.1: resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} micromark-factory-label@2.0.1: resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + micromark-factory-space@2.0.1: resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} @@ -1532,6 +1640,9 @@ packages: micromark-util-encode@2.0.1: resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + micromark-util-html-tag-name@2.0.1: resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} @@ -1556,8 +1667,8 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - miniflare@5.20260826.0-alpha: - resolution: {integrity: sha512-ZXR3Bieg+B5MK0T/zYIWaZiCGCb9Z3en4+/TleYKhIGPLx/e0cRcG/ZvvTviUapVBBK2zODB+aiypdIojU3x6Q==} + miniflare@5.20260903.0-alpha: + resolution: {integrity: sha512-VCZIFxOqFXeibRBJWwTlppqbv2lkeO10IX3QBRGK9j1QiMAfH/OSsCvbjp1MslBMhVZmy2VTRlVaVnsKnbDp6A==} engines: {node: '>=22.0.0'} mrmime@2.0.1: @@ -1611,8 +1722,8 @@ packages: oniguruma-to-es@4.3.6: resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} - p-limit@7.3.1: - resolution: {integrity: sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==} + p-limit@7.3.2: + resolution: {integrity: sha512-Ll0w3fU24vYpXoZmjjZIee6bJQDgG0oAyo1PdmFYI8UDwJJddaHAypxIH9avUu+t+lSsAwKVsb1jDCMIIChliw==} engines: {node: '>=20'} p-queue@9.3.3: @@ -1626,6 +1737,9 @@ packages: package-manager-detector@1.8.0: resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-latin@7.0.0: resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} @@ -1655,8 +1769,8 @@ packages: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} - postcss@8.5.26: - resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} prettier@3.9.6: @@ -1686,6 +1800,20 @@ packages: resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} engines: {node: '>= 20.19.0'} + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.1: + resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -1701,6 +1829,9 @@ packages: rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + rehype-slug@6.0.0: resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} @@ -1710,6 +1841,9 @@ packages: remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + remark-mdx@3.1.1: + resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} + remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} @@ -1748,8 +1882,8 @@ packages: retext@9.0.0: resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} - rolldown@1.2.6: - resolution: {integrity: sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==} + rolldown@1.2.7: + resolution: {integrity: sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -1794,12 +1928,13 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - stream-replace-string@2.0.0: - resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} - string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} @@ -1815,6 +1950,12 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -1831,8 +1972,8 @@ packages: resolution: {integrity: sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==} engines: {node: ^16.14.0 || >= 17.3.0} - tinyexec@1.3.0: - resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} engines: {node: '>=18'} tinyglobby@0.2.17: @@ -1874,8 +2015,8 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - undici@7.29.0: - resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} + undici@7.29.1: + resolution: {integrity: sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.24: @@ -1896,6 +2037,9 @@ packages: unist-util-modify-children@4.0.0: resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} @@ -2109,24 +2253,24 @@ packages: resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} engines: {node: '>=14.0.0'} - vscode-jsonrpc@9.0.1: - resolution: {integrity: sha512-rfuA6T75H6m5EkbhtEPzre9pT0HPcDI2MMy4+nPFIBks5J8JBAUHD4tRYSgaBOijIEC7SRkC1kKyXTLqbmh9jw==} + vscode-jsonrpc@9.0.2: + resolution: {integrity: sha512-SbQSV9yRemARxeXw6LU5sS6Zq0e9/DgCCX5yelH263ZQWukbTk8EF8fjTrr1dziasf4GwlJbvTwFnTrnQFWZXQ==} engines: {node: '>=14.0.0'} vscode-languageserver-protocol@3.17.5: resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} - vscode-languageserver-protocol@3.18.2: - resolution: {integrity: sha512-XRyDbT0Pp3sSNti3JmxVEUMySWCSi1hhM+/KUlCy1hV1zmrqpM1OwO12EAki8blhmLuIMpaJrYbo0OzGVfK2Qg==} + vscode-languageserver-protocol@3.18.3: + resolution: {integrity: sha512-DF49+WeV5py4zO5hhobp60jjsDSK0lAqA0OuKBLBvp423HPWQcCbhZz3JgyfIewsEz2f8U+X75xNIFHdiXZm2w==} - vscode-languageserver-textdocument@1.0.12: - resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + vscode-languageserver-textdocument@1.0.14: + resolution: {integrity: sha512-EQyqJMi552E4ZTf46izQ4Fj6XquqxCySR3J5ZSD1SisMf6RfpeOWHxGBE8Gr6V0/3GHIGdAzDn8F8+1nTGCnoQ==} vscode-languageserver-types@3.17.5: resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - vscode-languageserver-types@3.18.0: - resolution: {integrity: sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==} + vscode-languageserver-types@3.18.3: + resolution: {integrity: sha512-XIlzJ7Qp/jzSI1ds7/FwPAWrPeTZA7pAtlW4hdJ1J6xXWJL6dR9QYnDhJOdLzdKhUQ5Mm6mvUMw+3DcOQQasPw==} vscode-languageserver@9.0.1: resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} @@ -2135,23 +2279,23 @@ packages: vscode-nls@5.2.0: resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} - vscode-uri@3.1.0: - resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + vscode-uri@3.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - workerd@1.20260826.1: - resolution: {integrity: sha512-oTG9ot5zxO9OjjKCskt86+nVrBL0kiUqExHnYaTg4OCkHf/K4zr+9hYvgwmG8DdCWG0TQGErHzD+1h50TM5b+w==} + workerd@1.20260903.1: + resolution: {integrity: sha512-xJzt2RnCy7ulOULmZy/4JLbEPg1uisp9lVoOUsEz+UVhDsTmrSQ0rBXZMGcXuhr2HCGKs89bg8nnbbzvBSX1Ig==} engines: {node: '>=16'} hasBin: true - wrangler@4.127.0: - resolution: {integrity: sha512-4dPqcBEMJfGeZeNnjHT7ThNJs+EiNYxUTg4ywqIdQubcXHBhFeVMQyHV4A9AOhZRFr83cckqdds034KGcr/dtw==} + wrangler@4.129.0: + resolution: {integrity: sha512-PGPvs9UPoFrwxT0VogpESSZGvZIctAuTK3wGsLLPHtHsSgS85kNdvtpa2d14UzG8gwLWD64XUGFPGG9tOXG9VQ==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260826.1 + '@cloudflare/workers-types': ^5.20260903.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -2206,9 +2350,6 @@ packages: youch@4.1.0-beta.10: resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - zod@4.5.4: resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} @@ -2219,7 +2360,7 @@ snapshots: '@astrojs/check@0.9.10(prettier@3.9.6)(typescript@7.0.2)': dependencies: - '@astrojs/language-server': 2.16.14(prettier@3.9.6)(typescript@7.0.2) + '@astrojs/language-server': 2.16.16(prettier@3.9.6)(typescript@7.0.2) chokidar: 4.0.3 kleur: 4.1.5 typescript: 7.0.2 @@ -2284,7 +2425,7 @@ snapshots: '@astrojs/compiler@2.13.1': {} - '@astrojs/internal-helpers@0.10.4': + '@astrojs/internal-helpers@0.11.0': dependencies: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 @@ -2295,11 +2436,11 @@ snapshots: smol-toml: 1.8.0 unified: 11.0.5 - '@astrojs/language-server@2.16.14(prettier@3.9.6)(typescript@7.0.2)': + '@astrojs/language-server@2.16.16(prettier@3.9.6)(typescript@7.0.2)': dependencies: '@astrojs/compiler': 2.13.1 '@astrojs/yaml2ts': 0.2.4 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 '@volar/kit': 2.4.28(typescript@7.0.2) '@volar/language-core': 2.4.28 '@volar/language-server': 2.4.28 @@ -2314,18 +2455,22 @@ snapshots: volar-service-typescript-twoslash-queries: 0.0.71(@volar/language-service@2.4.28) volar-service-yaml: 0.0.71(@volar/language-service@2.4.28) vscode-html-languageservice: 5.6.2 - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 optionalDependencies: prettier: 3.9.6 transitivePeerDependencies: - typescript - '@astrojs/markdown-remark@7.2.4': + '@astrojs/markdown-remark@7.3.0': dependencies: - '@astrojs/internal-helpers': 0.10.4 + '@astrojs/internal-helpers': 0.11.0 '@astrojs/prism': 4.0.2 + '@mdx-js/mdx': 3.1.1 + acorn: 8.18.0 + estree-util-visit: 2.0.0 github-slugger: 2.0.0 hast-util-from-html: 2.0.3 + hast-util-to-html: 9.0.5 hast-util-to-text: 4.0.2 mdast-util-definitions: 6.0.0 rehype-raw: 7.0.0 @@ -2334,6 +2479,7 @@ snapshots: remark-parse: 11.0.0 remark-rehype: 11.1.2 remark-smartypants: 3.0.3 + source-map: 0.7.6 unified: 11.0.5 unist-util-remove-position: 5.0.0 unist-util-visit: 5.1.0 @@ -2342,9 +2488,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/markdown-satteri@0.3.8': + '@astrojs/markdown-satteri@0.4.0': dependencies: - '@astrojs/internal-helpers': 0.10.4 + '@astrojs/internal-helpers': 0.11.0 '@astrojs/prism': 4.0.2 github-slugger: 2.0.0 satteri: 0.10.5 @@ -2353,11 +2499,10 @@ snapshots: dependencies: prismjs: 1.30.0 - '@astrojs/sitemap@3.7.3': + '@astrojs/sitemap@3.7.4': dependencies: sitemap: 9.0.1 - stream-replace-string: 2.0.0 - zod: 4.4.3 + zod: 4.5.4 '@astrojs/telemetry@3.3.3': dependencies: @@ -2432,25 +2577,25 @@ snapshots: '@cloudflare/kv-asset-handler@0.5.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260826.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260903.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260826.1 + workerd: 1.20260903.1 - '@cloudflare/workerd-darwin-64@1.20260826.1': + '@cloudflare/workerd-darwin-64@1.20260903.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260826.1': + '@cloudflare/workerd-darwin-arm64@1.20260903.1': optional: true - '@cloudflare/workerd-linux-64@1.20260826.1': + '@cloudflare/workerd-linux-64@1.20260903.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260826.1': + '@cloudflare/workerd-linux-arm64@1.20260903.1': optional: true - '@cloudflare/workerd-windows-64@1.20260826.1': + '@cloudflare/workerd-windows-64@1.20260903.1': optional: true '@cspotcode/source-map-support@0.8.1': @@ -2579,7 +2724,7 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@everruns/bashkit-wasm@0.16.0': {} + '@everruns/bashkit-wasm@0.17.1': {} '@img/colour@1.1.0': {} @@ -2689,8 +2834,6 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/sourcemap-codec@1.6.0': {} '@jridgewell/trace-mapping@0.3.9': @@ -2698,6 +2841,36 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 + '@mdx-js/mdx@3.1.1': + dependencies: + '@types/estree': 1.0.9 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdx': 2.0.14 + acorn: 8.18.0 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.1 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.1(acorn@8.18.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.6 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -2707,7 +2880,7 @@ snapshots: '@oslojs/encoding@1.1.0': {} - '@oxc-project/types@0.147.0': {} + '@oxc-project/types@0.148.0': {} '@poppinss/colors@4.1.6': dependencies: @@ -2721,49 +2894,49 @@ snapshots: '@poppinss/exception@1.2.3': {} - '@rolldown/binding-android-arm-eabi@1.2.6': + '@rolldown/binding-android-arm-eabi@1.2.7': optional: true - '@rolldown/binding-android-arm64@1.2.6': + '@rolldown/binding-android-arm64@1.2.7': optional: true - '@rolldown/binding-darwin-arm64@1.2.6': + '@rolldown/binding-darwin-arm64@1.2.7': optional: true - '@rolldown/binding-darwin-x64@1.2.6': + '@rolldown/binding-darwin-x64@1.2.7': optional: true - '@rolldown/binding-freebsd-x64@1.2.6': + '@rolldown/binding-freebsd-x64@1.2.7': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.2.6': + '@rolldown/binding-linux-arm-gnueabihf@1.2.7': optional: true - '@rolldown/binding-linux-arm64-gnu@1.2.6': + '@rolldown/binding-linux-arm64-gnu@1.2.7': optional: true - '@rolldown/binding-linux-arm64-musl@1.2.6': + '@rolldown/binding-linux-arm64-musl@1.2.7': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.2.6': + '@rolldown/binding-linux-ppc64-gnu@1.2.7': optional: true - '@rolldown/binding-linux-s390x-gnu@1.2.6': + '@rolldown/binding-linux-s390x-gnu@1.2.7': optional: true - '@rolldown/binding-linux-x64-gnu@1.2.6': + '@rolldown/binding-linux-x64-gnu@1.2.7': optional: true - '@rolldown/binding-linux-x64-musl@1.2.6': + '@rolldown/binding-linux-x64-musl@1.2.7': optional: true - '@rolldown/binding-openharmony-arm64@1.2.6': + '@rolldown/binding-openharmony-arm64@1.2.7': optional: true - '@rolldown/binding-win32-arm64-msvc@1.2.6': + '@rolldown/binding-win32-arm64-msvc@1.2.7': optional: true - '@rolldown/binding-win32-x64-msvc@1.2.6': + '@rolldown/binding-win32-x64-msvc@1.2.7': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -2835,6 +3008,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/mdx@2.0.14': {} + '@types/ms@2.1.0': {} '@types/nlcst@2.0.3': @@ -2845,13 +3020,15 @@ snapshots: dependencies: undici-types: 7.18.2 - '@types/node@26.4.0': + '@types/node@26.4.1': dependencies: undici-types: 8.3.0 '@types/sax@1.2.7': dependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 + + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -2915,7 +3092,7 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - '@ungap/structured-clone@1.3.3': {} + '@ungap/structured-clone@1.4.0': {} '@volar/kit@2.4.28(typescript@7.0.2)': dependencies: @@ -2923,8 +3100,8 @@ snapshots: '@volar/typescript': 2.4.28 typesafe-path: 0.2.2 typescript: 7.0.2 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 + vscode-languageserver-textdocument: 1.0.14 + vscode-uri: 3.2.0 '@volar/language-core@2.4.28': dependencies: @@ -2938,16 +3115,16 @@ snapshots: path-browserify: 1.0.1 request-light: 0.7.0 vscode-languageserver: 9.0.1 - vscode-languageserver-protocol: 3.18.2 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 + vscode-languageserver-protocol: 3.18.3 + vscode-languageserver-textdocument: 1.0.14 + vscode-uri: 3.2.0 '@volar/language-service@2.4.28': dependencies: '@volar/language-core': 2.4.28 - vscode-languageserver-protocol: 3.18.2 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 + vscode-languageserver-protocol: 3.18.3 + vscode-languageserver-textdocument: 1.0.14 + vscode-uri: 3.2.0 '@volar/source-map@2.4.28': {} @@ -2955,18 +3132,24 @@ snapshots: dependencies: '@volar/language-core': 2.4.28 path-browserify: 1.0.1 - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 '@vscode/emmet-helper@2.11.0': dependencies: emmet: 2.4.11 jsonc-parser: 2.3.1 - vscode-languageserver-textdocument: 1.0.12 - vscode-languageserver-types: 3.18.0 - vscode-uri: 3.1.0 + vscode-languageserver-textdocument: 1.0.14 + vscode-languageserver-types: 3.18.3 + vscode-uri: 3.2.0 '@vscode/l10n@0.0.18': {} + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + ajv-draft-04@1.0.0(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -2978,7 +3161,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.5 + fast-uri: 3.1.7 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -3003,11 +3186,13 @@ snapshots: array-iterate@2.0.1: {} - astro@7.2.9(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.4.0)(yaml@2.9.0): + astring@1.9.0: {} + + astro@7.3.1(@astrojs/markdown-remark@7.3.0)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@26.4.1)(yaml@2.9.0): dependencies: '@astrojs/compiler-rs': 0.4.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) - '@astrojs/internal-helpers': 0.10.4 - '@astrojs/markdown-satteri': 0.3.8 + '@astrojs/internal-helpers': 0.11.0 + '@astrojs/markdown-satteri': 0.4.0 '@astrojs/telemetry': 3.3.3 '@capsizecss/unpack': 4.0.1 '@clack/prompts': 1.7.0 @@ -3038,7 +3223,7 @@ snapshots: mrmime: 2.0.1 neotraverse: 1.0.1 obug: 2.1.4 - p-limit: 7.3.1 + p-limit: 7.3.2 p-queue: 9.3.3 package-manager-detector: 1.8.0 piccolore: 0.1.3 @@ -3048,19 +3233,19 @@ snapshots: smol-toml: 1.8.0 svgo: 4.1.0 tinyclip: 0.1.15 - tinyexec: 1.3.0 + tinyexec: 1.3.1 tinyglobby: 0.2.17 ultrahtml: 1.7.0 unifont: 0.7.5 unstorage: 1.17.5 - vite: 8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(yaml@2.9.0) - vitefu: 1.1.3(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(yaml@2.9.0)) + vite: 8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.5.4 optionalDependencies: - '@astrojs/markdown-remark': 7.2.4 - sharp: 0.35.4(@types/node@26.4.0) + '@astrojs/markdown-remark': 7.3.0 + sharp: 0.35.4(@types/node@26.4.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -3111,6 +3296,8 @@ snapshots: character-entities@2.0.2: {} + character-reference-invalid@2.0.1: {} + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -3129,6 +3316,8 @@ snapshots: clsx@2.1.1: {} + collapse-white-space@2.1.0: {} + comma-separated-tokens@2.0.3: {} commander@11.1.0: {} @@ -3228,6 +3417,20 @@ snapshots: es-module-lexer@2.3.2: {} + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.18.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.3 + esbuild@0.28.2: optionalDependencies: '@esbuild/aix-ppc64': 0.28.2 @@ -3261,6 +3464,39 @@ snapshots: escape-string-regexp@5.0.0: {} + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.9 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.1: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.6 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + eventemitter3@5.0.4: {} extend@3.0.2: {} @@ -3273,7 +3509,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.5: {} + fast-uri@3.1.7: {} fast-wrap-ansi@0.2.2: dependencies: @@ -3356,7 +3592,7 @@ snapshots: dependencies: '@types/hast': 3.0.5 '@types/unist': 3.0.3 - '@ungap/structured-clone': 1.3.3 + '@ungap/structured-clone': 1.4.0 hast-util-from-parse5: 8.0.3 hast-util-to-parse5: 8.0.1 html-void-elements: 3.0.0 @@ -3368,6 +3604,27 @@ snapshots: web-namespaces: 2.0.1 zwitch: 2.0.4 + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.9 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + hast-util-to-html@9.0.5: dependencies: '@types/hast': 3.0.5 @@ -3382,6 +3639,26 @@ snapshots: stringify-entities: 4.0.4 zwitch: 2.0.4 + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + hast-util-to-parse5@8.0.1: dependencies: '@types/hast': 3.0.5 @@ -3421,10 +3698,23 @@ snapshots: http-cache-semantics@4.2.0: {} + inline-style-parser@0.2.7: {} + iron-webcrypto@1.2.1: {} + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + is-docker@4.0.0: {} + is-hexadecimal@2.0.1: {} + is-plain-obj@4.1.0: {} js-yaml@4.3.2: @@ -3502,6 +3792,8 @@ snapshots: '@babel/types': 7.29.8 source-map-js: 1.2.1 + markdown-extensions@2.0.0: {} + markdown-table@3.0.4: {} mdast-util-definitions@6.0.0: @@ -3591,6 +3883,55 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + mdast-util-phrasing@4.1.0: dependencies: '@types/mdast': 4.0.4 @@ -3600,7 +3941,7 @@ snapshots: dependencies: '@types/hast': 3.0.5 '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.3 + '@ungap/structured-clone': 1.4.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 @@ -3705,6 +4046,57 @@ snapshots: micromark-util-combine-extensions: 2.0.1 micromark-util-types: 2.0.2 + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + micromark-factory-destination@2.0.1: dependencies: micromark-util-character: 2.1.1 @@ -3718,6 +4110,18 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + micromark-factory-space@2.0.1: dependencies: micromark-util-character: 2.1.1 @@ -3770,6 +4174,16 @@ snapshots: micromark-util-encode@2.0.1: {} + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.9 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + micromark-util-html-tag-name@2.0.1: {} micromark-util-normalize-identifier@2.0.1: @@ -3819,12 +4233,12 @@ snapshots: transitivePeerDependencies: - supports-color - miniflare@5.20260826.0-alpha(@types/node@26.4.0): + miniflare@5.20260903.0-alpha(@types/node@26.4.1): dependencies: '@cspotcode/source-map-support': 0.8.1 - sharp: 0.35.4(@types/node@26.4.0) - undici: 7.29.0 - workerd: 1.20260826.1 + sharp: 0.35.4(@types/node@26.4.1) + undici: 7.29.1 + workerd: 1.20260903.1 ws: 8.21.3 youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -3874,7 +4288,7 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 - p-limit@7.3.1: + p-limit@7.3.2: dependencies: yocto-queue: 1.2.2 @@ -3887,6 +4301,16 @@ snapshots: package-manager-detector@1.8.0: {} + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse-latin@7.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -3914,7 +4338,7 @@ snapshots: picomatch@4.0.7: {} - postcss@8.5.26: + postcss@8.5.28: dependencies: nanoid: 3.3.18 picocolors: 1.1.1 @@ -3934,6 +4358,35 @@ snapshots: readdirp@5.1.1: {} + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.9 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.1(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.9 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.9 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -3947,7 +4400,7 @@ snapshots: rehype-autolink-headings@7.1.0: dependencies: '@types/hast': 3.0.5 - '@ungap/structured-clone': 1.3.3 + '@ungap/structured-clone': 1.4.0 hast-util-heading-rank: 3.0.0 hast-util-is-element: 3.0.0 unified: 11.0.5 @@ -3959,6 +4412,14 @@ snapshots: hast-util-raw: 9.1.0 vfile: 6.0.3 + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + rehype-slug@6.0.0: dependencies: '@types/hast': 3.0.5 @@ -3984,6 +4445,13 @@ snapshots: transitivePeerDependencies: - supports-color + remark-mdx@3.1.1: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 @@ -4047,26 +4515,26 @@ snapshots: retext-stringify: 4.0.0 unified: 11.0.5 - rolldown@1.2.6: + rolldown@1.2.7: dependencies: - '@oxc-project/types': 0.147.0 + '@oxc-project/types': 0.148.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm-eabi': 1.2.6 - '@rolldown/binding-android-arm64': 1.2.6 - '@rolldown/binding-darwin-arm64': 1.2.6 - '@rolldown/binding-darwin-x64': 1.2.6 - '@rolldown/binding-freebsd-x64': 1.2.6 - '@rolldown/binding-linux-arm-gnueabihf': 1.2.6 - '@rolldown/binding-linux-arm64-gnu': 1.2.6 - '@rolldown/binding-linux-arm64-musl': 1.2.6 - '@rolldown/binding-linux-ppc64-gnu': 1.2.6 - '@rolldown/binding-linux-s390x-gnu': 1.2.6 - '@rolldown/binding-linux-x64-gnu': 1.2.6 - '@rolldown/binding-linux-x64-musl': 1.2.6 - '@rolldown/binding-openharmony-arm64': 1.2.6 - '@rolldown/binding-win32-arm64-msvc': 1.2.6 - '@rolldown/binding-win32-x64-msvc': 1.2.6 + '@rolldown/binding-android-arm-eabi': 1.2.7 + '@rolldown/binding-android-arm64': 1.2.7 + '@rolldown/binding-darwin-arm64': 1.2.7 + '@rolldown/binding-darwin-x64': 1.2.7 + '@rolldown/binding-freebsd-x64': 1.2.7 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.7 + '@rolldown/binding-linux-arm64-gnu': 1.2.7 + '@rolldown/binding-linux-arm64-musl': 1.2.7 + '@rolldown/binding-linux-ppc64-gnu': 1.2.7 + '@rolldown/binding-linux-s390x-gnu': 1.2.7 + '@rolldown/binding-linux-x64-gnu': 1.2.7 + '@rolldown/binding-linux-x64-musl': 1.2.7 + '@rolldown/binding-openharmony-arm64': 1.2.7 + '@rolldown/binding-win32-arm64-msvc': 1.2.7 + '@rolldown/binding-win32-x64-msvc': 1.2.7 satteri@0.10.5: dependencies: @@ -4089,7 +4557,7 @@ snapshots: semver@7.8.5: {} - sharp@0.35.4(@types/node@26.4.0): + sharp@0.35.4(@types/node@26.4.1): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 @@ -4120,7 +4588,7 @@ snapshots: '@img/sharp-win32-arm64': 0.35.4 '@img/sharp-win32-ia32': 0.35.4 '@img/sharp-win32-x64': 0.35.4 - '@types/node': 26.4.0 + '@types/node': 26.4.1 shiki@4.4.3: dependencies: @@ -4146,9 +4614,9 @@ snapshots: source-map-js@1.2.1: {} - space-separated-tokens@2.0.2: {} + source-map@0.7.6: {} - stream-replace-string@2.0.0: {} + space-separated-tokens@2.0.2: {} string-width@7.2.0: dependencies: @@ -4170,6 +4638,14 @@ snapshots: dependencies: ansi-regex: 6.3.0 + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + supports-color@10.2.2: {} svgo@4.1.0: @@ -4186,7 +4662,7 @@ snapshots: tinyclip@0.1.15: {} - tinyexec@1.3.0: {} + tinyexec@1.3.1: {} tinyglobby@0.2.17: dependencies: @@ -4239,7 +4715,7 @@ snapshots: undici-types@8.3.0: {} - undici@7.29.0: {} + undici@7.29.1: {} unenv@2.0.0-rc.24: dependencies: @@ -4259,7 +4735,7 @@ snapshots: dependencies: css-tree: 3.2.1 ohash: 2.0.12 - undici: 7.29.0 + undici: 7.29.1 unist-util-find-after@5.0.0: dependencies: @@ -4275,6 +4751,10 @@ snapshots: '@types/unist': 3.0.3 array-iterate: 2.0.1 + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-position@5.0.0: dependencies: '@types/unist': 3.0.3 @@ -4329,28 +4809,28 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(yaml@2.9.0): + vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 - postcss: 8.5.26 - rolldown: 1.2.6 + postcss: 8.5.28 + rolldown: 1.2.7 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 esbuild: 0.28.2 fsevents: 2.3.3 yaml: 2.9.0 - vitefu@1.1.3(vite@8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(yaml@2.9.0)): optionalDependencies: - vite: 8.2.2(@types/node@26.4.0)(esbuild@0.28.2)(yaml@2.9.0) + vite: 8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(yaml@2.9.0) volar-service-css@0.0.71(@volar/language-service@2.4.28): dependencies: vscode-css-languageservice: 6.3.10 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 + vscode-languageserver-textdocument: 1.0.14 + vscode-uri: 3.2.0 optionalDependencies: '@volar/language-service': 2.4.28 @@ -4359,28 +4839,28 @@ snapshots: '@emmetio/css-parser': 0.4.1 '@emmetio/html-matcher': 1.3.0 '@vscode/emmet-helper': 2.11.0 - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 optionalDependencies: '@volar/language-service': 2.4.28 volar-service-html@0.0.71(@volar/language-service@2.4.28): dependencies: vscode-html-languageservice: 5.6.2 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 + vscode-languageserver-textdocument: 1.0.14 + vscode-uri: 3.2.0 optionalDependencies: '@volar/language-service': 2.4.28 volar-service-prettier@0.0.71(@volar/language-service@2.4.28)(prettier@3.9.6): dependencies: - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 optionalDependencies: '@volar/language-service': 2.4.28 prettier: 3.9.6 volar-service-typescript-twoslash-queries@0.0.71(@volar/language-service@2.4.28): dependencies: - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 optionalDependencies: '@volar/language-service': 2.4.28 @@ -4389,15 +4869,15 @@ snapshots: path-browserify: 1.0.1 semver: 7.8.5 typescript-auto-import-cache: 0.3.6 - vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-textdocument: 1.0.14 vscode-nls: 5.2.0 - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 optionalDependencies: '@volar/language-service': 2.4.28 volar-service-yaml@0.0.71(@volar/language-service@2.4.28): dependencies: - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 yaml-language-server: 1.23.0 optionalDependencies: '@volar/language-service': 2.4.28 @@ -4405,44 +4885,44 @@ snapshots: vscode-css-languageservice@6.3.10: dependencies: '@vscode/l10n': 0.0.18 - vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-textdocument: 1.0.14 vscode-languageserver-types: 3.17.5 - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 vscode-html-languageservice@5.6.2: dependencies: '@vscode/l10n': 0.0.18 - vscode-languageserver-textdocument: 1.0.12 - vscode-languageserver-types: 3.18.0 - vscode-uri: 3.1.0 + vscode-languageserver-textdocument: 1.0.14 + vscode-languageserver-types: 3.18.3 + vscode-uri: 3.2.0 vscode-json-languageservice@4.1.8: dependencies: jsonc-parser: 3.3.1 - vscode-languageserver-textdocument: 1.0.12 - vscode-languageserver-types: 3.18.0 + vscode-languageserver-textdocument: 1.0.14 + vscode-languageserver-types: 3.18.3 vscode-nls: 5.2.0 - vscode-uri: 3.1.0 + vscode-uri: 3.2.0 vscode-jsonrpc@8.2.0: {} - vscode-jsonrpc@9.0.1: {} + vscode-jsonrpc@9.0.2: {} vscode-languageserver-protocol@3.17.5: dependencies: vscode-jsonrpc: 8.2.0 vscode-languageserver-types: 3.17.5 - vscode-languageserver-protocol@3.18.2: + vscode-languageserver-protocol@3.18.3: dependencies: - vscode-jsonrpc: 9.0.1 - vscode-languageserver-types: 3.18.0 + vscode-jsonrpc: 9.0.2 + vscode-languageserver-types: 3.18.3 - vscode-languageserver-textdocument@1.0.12: {} + vscode-languageserver-textdocument@1.0.14: {} vscode-languageserver-types@3.17.5: {} - vscode-languageserver-types@3.18.0: {} + vscode-languageserver-types@3.18.3: {} vscode-languageserver@9.0.1: dependencies: @@ -4450,28 +4930,28 @@ snapshots: vscode-nls@5.2.0: {} - vscode-uri@3.1.0: {} + vscode-uri@3.2.0: {} web-namespaces@2.0.1: {} - workerd@1.20260826.1: + workerd@1.20260903.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260826.1 - '@cloudflare/workerd-darwin-arm64': 1.20260826.1 - '@cloudflare/workerd-linux-64': 1.20260826.1 - '@cloudflare/workerd-linux-arm64': 1.20260826.1 - '@cloudflare/workerd-windows-64': 1.20260826.1 + '@cloudflare/workerd-darwin-64': 1.20260903.1 + '@cloudflare/workerd-darwin-arm64': 1.20260903.1 + '@cloudflare/workerd-linux-64': 1.20260903.1 + '@cloudflare/workerd-linux-arm64': 1.20260903.1 + '@cloudflare/workerd-windows-64': 1.20260903.1 - wrangler@4.127.0(@types/node@26.4.0): + wrangler@4.129.0(@types/node@26.4.1): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260826.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260903.1) blake3-wasm: 2.1.5 esbuild: 0.28.2 - miniflare: 5.20260826.0-alpha(@types/node@26.4.0) + miniflare: 5.20260903.0-alpha(@types/node@26.4.1) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260826.1 + workerd: 1.20260903.1 optionalDependencies: fsevents: 2.3.3 transitivePeerDependencies: @@ -4501,9 +4981,9 @@ snapshots: request-light: 0.5.8 vscode-json-languageservice: 4.1.8 vscode-languageserver: 9.0.1 - vscode-languageserver-textdocument: 1.0.12 - vscode-languageserver-types: 3.18.0 - vscode-uri: 3.1.0 + vscode-languageserver-textdocument: 1.0.14 + vscode-languageserver-types: 3.18.3 + vscode-uri: 3.2.0 yaml: 2.9.0 yaml@2.9.0: {} @@ -4534,8 +5014,6 @@ snapshots: cookie: 1.1.1 youch-core: 0.3.3 - zod@4.4.3: {} - zod@4.5.4: {} zwitch@2.0.4: {} diff --git a/site/src/content/apidocs/python.md b/site/src/content/apidocs/python.md index e1f500335..3cf81a669 100644 --- a/site/src/content/apidocs/python.md +++ b/site/src/content/apidocs/python.md @@ -1732,6 +1732,8 @@ True - **`stderr`** — `str` - **`exit_code`** — `int` - **`error`** — `str | None` +- **`stdout_truncated`** — `bool` +- **`stderr_truncated`** — `bool` - **`success`** — `bool` ### `to_dict` diff --git a/site/src/data/performance-timeline.json b/site/src/data/performance-timeline.json index a6a0dd98d..11283d3b8 100644 --- a/site/src/data/performance-timeline.json +++ b/site/src/data/performance-timeline.json @@ -1,26 +1,26 @@ { - "generatedAt": "2026-09-02T18:17:29.000Z", + "generatedAt": "2026-09-05T17:37:13.000Z", "sources": { "bench": "crates/bashkit-bench/results/*.json", "criterion": "crates/bashkit/benches/results/*.md", "evals": "crates/bashkit-eval/results/*.json" }, "summary": { - "benchRuns": 9, - "criterionRuns": 13, + "benchRuns": 10, + "criterionRuns": 15, "evalRuns": 38, "latestBench": { - "id": "bench-vm-linux-x86_64-1788373049", + "id": "bench-mykhailosmac-macos-aarch64-1788629833", "kind": "bashkit-bench", - "label": "vm-linux-x86_64", - "date": "2026-09-02", - "timestamp": "2026-09-02T18:17:29.000Z", - "source": "crates/bashkit-bench/results/bench-vm-linux-x86_64-1788373049.json", - "reportSource": "crates/bashkit-bench/results/bench-vm-linux-x86_64-1788373049.md", + "label": "mykhailosmac-macos-aarch64", + "date": "2026-09-05", + "timestamp": "2026-09-05T17:37:13.000Z", + "source": "crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.json", + "reportSource": "crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.md", "cases": 96, - "speedup": 20.7, - "bashkitMs": 51.73, - "bashMs": 1072.71, + "speedup": 49.9, + "bashkitMs": 20.16, + "bashMs": 1004.86, "errorRate": 0, "matchRate": 100, "categories": [ @@ -28,97 +28,97 @@ "category": "startup", "description": "Small commands where interpreter startup dominates runtime.", "cases": 4, - "bashkitMedianMs": 0.046, - "bashMedianMs": 2.231, - "speedup": 49.5 + "bashkitMedianMs": 0.016, + "bashMedianMs": 4.769, + "speedup": 293.2 + }, + { + "category": "arithmetic", + "description": "Integer math, substitutions, and expression-heavy shell snippets.", + "cases": 6, + "bashkitMedianMs": 0.018, + "bashMedianMs": 4.313, + "speedup": 215.7 }, { "category": "strings", "description": "String expansion, pattern handling, and text manipulation.", "cases": 8, - "bashkitMedianMs": 0.051, - "bashMedianMs": 2.525, - "speedup": 47.8 + "bashkitMedianMs": 0.018, + "bashMedianMs": 4.378, + "speedup": 223.6 }, { "category": "variables", "description": "Variable assignment, lookup, expansion, and environment handling.", "cases": 8, - "bashkitMedianMs": 0.063, - "bashMedianMs": 2.294, - "speedup": 35.9 + "bashkitMedianMs": 0.019, + "bashMedianMs": 4.096, + "speedup": 213.5 }, { "category": "arrays", "description": "Indexed array reads, writes, expansion, and iteration.", "cases": 6, - "bashkitMedianMs": 0.063, - "bashMedianMs": 2.546, - "speedup": 40.7 - }, - { - "category": "arithmetic", - "description": "Integer math, substitutions, and expression-heavy shell snippets.", - "cases": 6, - "bashkitMedianMs": 0.069, - "bashMedianMs": 2.262, - "speedup": 32.8 + "bashkitMedianMs": 0.019, + "bashMedianMs": 4.064, + "speedup": 209.2 }, { "category": "pipes", "description": "Pipeline construction, streaming, and command chaining.", "cases": 6, - "bashkitMedianMs": 0.07, - "bashMedianMs": 4.866, - "speedup": 48 + "bashkitMedianMs": 0.021, + "bashMedianMs": 5.898, + "speedup": 264.2 }, { "category": "subshell", "description": "Command substitution and nested shell execution paths.", "cases": 6, - "bashkitMedianMs": 0.08, - "bashMedianMs": 4.838, - "speedup": 53 + "bashkitMedianMs": 0.024, + "bashMedianMs": 5.484, + "speedup": 233.3 }, { "category": "control", "description": "Conditionals, loops, case statements, and branching scripts.", "cases": 9, - "bashkitMedianMs": 0.087, - "bashMedianMs": 2.464, - "speedup": 28.4 - }, - { - "category": "tools", - "description": "Builtin and external-tool style command workloads.", - "cases": 21, - "bashkitMedianMs": 0.093, - "bashMedianMs": 4.893, - "speedup": 51.2 + "bashkitMedianMs": 0.025, + "bashMedianMs": 3.812, + "speedup": 171.4 }, { "category": "io", "description": "File reads, writes, redirects, and filesystem-facing commands.", "cases": 6, - "bashkitMedianMs": 0.104, - "bashMedianMs": 3.548, - "speedup": 32.6 + "bashkitMedianMs": 0.027, + "bashMedianMs": 5.995, + "speedup": 247.3 + }, + { + "category": "tools", + "description": "Builtin and external-tool style command workloads.", + "cases": 21, + "bashkitMedianMs": 0.031, + "bashMedianMs": 7.186, + "speedup": 216.7 }, { "category": "complex", "description": "Mixed shell workflows that combine multiple language features.", "cases": 7, - "bashkitMedianMs": 0.133, - "bashMedianMs": 5.067, - "speedup": 19.9 + "bashkitMedianMs": 0.065, + "bashMedianMs": 5.276, + "speedup": 78.3 }, { "category": "large", "description": "Bigger scripts and higher-volume data paths.", "cases": 9, - "bashkitMedianMs": 1.752, - "bashMedianMs": 3.951, - "speedup": 5.9 + "bashkitMedianMs": 0.741, + "bashMedianMs": 4.593, + "speedup": 20.1 } ] }, @@ -1398,6 +1398,119 @@ "speedup": 5.9 } ] + }, + { + "id": "bench-mykhailosmac-macos-aarch64-1788629833", + "kind": "bashkit-bench", + "label": "mykhailosmac-macos-aarch64", + "date": "2026-09-05", + "timestamp": "2026-09-05T17:37:13.000Z", + "source": "crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.json", + "reportSource": "crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.md", + "cases": 96, + "speedup": 49.9, + "bashkitMs": 20.16, + "bashMs": 1004.86, + "errorRate": 0, + "matchRate": 100, + "categories": [ + { + "category": "startup", + "description": "Small commands where interpreter startup dominates runtime.", + "cases": 4, + "bashkitMedianMs": 0.016, + "bashMedianMs": 4.769, + "speedup": 293.2 + }, + { + "category": "arithmetic", + "description": "Integer math, substitutions, and expression-heavy shell snippets.", + "cases": 6, + "bashkitMedianMs": 0.018, + "bashMedianMs": 4.313, + "speedup": 215.7 + }, + { + "category": "strings", + "description": "String expansion, pattern handling, and text manipulation.", + "cases": 8, + "bashkitMedianMs": 0.018, + "bashMedianMs": 4.378, + "speedup": 223.6 + }, + { + "category": "variables", + "description": "Variable assignment, lookup, expansion, and environment handling.", + "cases": 8, + "bashkitMedianMs": 0.019, + "bashMedianMs": 4.096, + "speedup": 213.5 + }, + { + "category": "arrays", + "description": "Indexed array reads, writes, expansion, and iteration.", + "cases": 6, + "bashkitMedianMs": 0.019, + "bashMedianMs": 4.064, + "speedup": 209.2 + }, + { + "category": "pipes", + "description": "Pipeline construction, streaming, and command chaining.", + "cases": 6, + "bashkitMedianMs": 0.021, + "bashMedianMs": 5.898, + "speedup": 264.2 + }, + { + "category": "subshell", + "description": "Command substitution and nested shell execution paths.", + "cases": 6, + "bashkitMedianMs": 0.024, + "bashMedianMs": 5.484, + "speedup": 233.3 + }, + { + "category": "control", + "description": "Conditionals, loops, case statements, and branching scripts.", + "cases": 9, + "bashkitMedianMs": 0.025, + "bashMedianMs": 3.812, + "speedup": 171.4 + }, + { + "category": "io", + "description": "File reads, writes, redirects, and filesystem-facing commands.", + "cases": 6, + "bashkitMedianMs": 0.027, + "bashMedianMs": 5.995, + "speedup": 247.3 + }, + { + "category": "tools", + "description": "Builtin and external-tool style command workloads.", + "cases": 21, + "bashkitMedianMs": 0.031, + "bashMedianMs": 7.186, + "speedup": 216.7 + }, + { + "category": "complex", + "description": "Mixed shell workflows that combine multiple language features.", + "cases": 7, + "bashkitMedianMs": 0.065, + "bashMedianMs": 5.276, + "speedup": 78.3 + }, + { + "category": "large", + "description": "Bigger scripts and higher-volume data paths.", + "cases": 9, + "bashkitMedianMs": 0.741, + "bashMedianMs": 4.593, + "speedup": 20.1 + } + ] } ], "criterionRuns": [ @@ -1676,6 +1789,48 @@ "us": 1428.9 }, "bestImprovement": null + }, + { + "id": "criterion-parallel-Mykhailos-Mac-mini.local-darwin-arm64-1788628263", + "kind": "criterion", + "family": "parallel", + "label": "Criterion Parallel Execution Benchmark", + "date": "2026-09-05", + "timestamp": "2026-09-05T17:11:03.000Z", + "source": "crates/bashkit/benches/results/criterion-parallel-Mykhailos-Mac-mini.local-darwin-arm64-1788628263.md", + "reportSource": "crates/bashkit/benches/results/criterion-parallel-Mykhailos-Mac-mini.local-darwin-arm64-1788628263.md", + "cases": 33, + "medianUs": 2078.5, + "p95Us": 52804, + "medianChangePct": null, + "meanChangePct": null, + "bestChangePct": null, + "fastestCase": { + "name": "single_bash_new", + "us": 12.55 + }, + "bestImprovement": null + }, + { + "id": "criterion-sqlite-Mykhailos-Mac-mini.local-darwin-arm64-1788628889", + "kind": "criterion", + "family": "sqlite", + "label": "Criterion SQLite Builtin Benchmark", + "date": "2026-09-05", + "timestamp": "2026-09-05T17:21:29.000Z", + "source": "crates/bashkit/benches/results/criterion-sqlite-Mykhailos-Mac-mini.local-darwin-arm64-1788628889.md", + "reportSource": "crates/bashkit/benches/results/criterion-sqlite-Mykhailos-Mac-mini.local-darwin-arm64-1788628889.md", + "cases": 44, + "medianUs": 1552.25, + "p95Us": 12070.7, + "medianChangePct": null, + "meanChangePct": null, + "bestChangePct": null, + "fastestCase": { + "name": "sqlite_query/aggregate_in_memory/100", + "us": 370.97 + }, + "bestImprovement": null } ], "evalRuns": [ @@ -5458,6 +5613,33 @@ "detail": "96 parity/perf cases on vm-linux-x86_64; output match 100%.", "metric": 20.7, "source": "crates/bashkit-bench/results/bench-vm-linux-x86_64-1788373049.json" + }, + { + "date": "2026-09-05", + "timestamp": "2026-09-05T17:11:03.000Z", + "kind": "Criterion", + "title": "parallel", + "detail": "single_bash_new at 12.55 us median", + "metric": 2078.5, + "source": "crates/bashkit/benches/results/criterion-parallel-Mykhailos-Mac-mini.local-darwin-arm64-1788628263.md" + }, + { + "date": "2026-09-05", + "timestamp": "2026-09-05T17:21:29.000Z", + "kind": "Criterion", + "title": "sqlite", + "detail": "sqlite_query/aggregate_in_memory/100 at 370.97 us median", + "metric": 1552.25, + "source": "crates/bashkit/benches/results/criterion-sqlite-Mykhailos-Mac-mini.local-darwin-arm64-1788628889.md" + }, + { + "date": "2026-09-05", + "timestamp": "2026-09-05T17:37:13.000Z", + "kind": "Benchmark", + "title": "49.9x faster than bash", + "detail": "96 parity/perf cases on mykhailosmac-macos-aarch64; output match 100%.", + "metric": 49.9, + "source": "crates/bashkit-bench/results/bench-mykhailosmac-macos-aarch64-1788629833.json" } ] } diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 66201f205..a43983c97 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -1,11 +1,65 @@ # cargo-vet audits file +[[audits.aes]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.9.2 -> 0.9.3" +notes = "Reviewed SIMD dispatch, loads/stores, fixed key sizes and partial-load zero padding. Typed batch bounds and divisibility assertions preserve memory limits. VAES dispatch assumes Rust target-feature contract vaes implies aes and avx2; inconsistent hostile CPUID outside review. No new ambient capability." + +[[audits.aes-gcm]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.11.0 -> 0.11.1" +notes = "Reviewed tag comparison replacement with ctutils CtEq: comparison before decrypt and truncated tag lengths preserved. Zeroize propagates to AES/GHASH and marker requires AES ZeroizeOnDrop. No new unsafe or ambient access." + +[[audits.antithesis_sdk]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.2.9 -> 0.3.0" +notes = "Reviewed assertion messages remaining compile-time strings; RwLock read guard drops before map insertion. Diagnostic output appends instead of truncating existing host-configured path. No new destinations or dynamic-loader changes." + +[[audits.argon2]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.6.0-rc.8 -> 0.6.0" +notes = "Reviewed runtime delta: equivalent map of u32 to little-endian bytes, semicolon, documentation/lints and must_use attributes. Memory unsafe code unchanged. Blake2 dependency promoted stable; nondefault output verification test added. No new ambient capability." + +[[audits.aws-lc-rs]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.18.0 -> 1.18.1" +notes = "Reviewed AEAD FFI pointer/capacity derived from same AsMut slice with checked Extend growth; constructors validate exact IV contexts. Shared secrets use Zeroizing Vec on failures; RSA digest validation centralized; HKDF salt algorithm consistent; legacy SHA1 explicit opt-in only. Standard FFI preconditions preserved; no new ambient access." + [[audits.aws-lc-sys]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" version = "0.39.1" +[[audits.aws-lc-sys]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.44.0 -> 0.45.0" +notes = "Reviewed C/FFI and generated symbol-prefix delta. EVP buffers final padded block; GCM checks IV length before subtraction; ML-DSA exact signature/extmu sizes, ML-KEM constant-time validation and parameter bounds retained. Keccak contexts bounded and cleansed; Brainpool immutable once groups. Builder flags preserve jitterentropy O0; fixed CPU discovery and existing compiler toolchain only. Source safety review, not cryptographic proof." + +[[audits.blake2]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.11.0-rc.6 -> 0.11.0" +notes = "Reviewed removal of broken nightly SIMD: lane math remains wrapping add/xor/rotate/reorder. New byte view borrows repr(C) four same-type u32/u64 fields, fully initialized without padding; output copies equal-size halves and zeroization retained. No new ambient access." + +[[audits.branches]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.4.5 -> 0.4.6" +notes = "Reviewed compiler gates for s390x and PowerPC prefetch. Assembly issues cache hints without dereference or stores, with matching architecture register constraints. No new ambient access." + +[[audits.cc]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.4.3 -> 1.4.5" +notes = "Reviewed Build env propagation to compiler and archiver probes without shell interpretation; environment enters compiler cache key. Missing OUT_DIR uses existing random create_new temporary file, Unix0600 and owned-path cleanup. OnceLock initialized check remains before unsafe borrow." + [[audits.clap]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -18,6 +72,24 @@ criteria = "safe-to-deploy" delta = "4.6.0 -> 4.6.1" notes = "Reviewed delta: derive macro emits compile-time env! guards for Cargo metadata and otherwise metadata/dependency updates; crate forbids unsafe code and adds no new runtime capabilities." +[[audits.combine]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "4.6.7 -> 4.6.8" +notes = "Reviewed BytesMut spare capacity sliced to at most 8KiB and zeroed before Read receives byte slice; write pointer length equals checked slice length. Other changes lifetimes/macros/derivations; no new ambient access." + +[[audits.cpufeatures]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.3.0 -> 0.3.1" +notes = "Reviewed complete source delta: AVX detection now requires YMM OS/XCR0 state instead of XMM, correctly preventing dispatch before OS support. Miri uses compile-time feature configuration, and the AtomicU8 sentinel remains the same value. No new unsafe operation or ambient capability." + +[[audits.crc32fast]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.5.0 -> 1.5.1" +notes = "Reviewed SIMD load guards and initialized tail buffers; CPU dispatch checks required features and AArch64 slices remain bounded. Wide CRC casts have documented indeterminate upper lanes but pinned Rust1.95 stdarch zeroes these lanes; re-review this assumption on toolchain change. No new ambient runtime access." + [[audits.ctor]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -30,6 +102,12 @@ criteria = "safe-to-deploy" delta = "0.0.7 -> 0.0.13" notes = "Reviewed delta: crate metadata only; generated code behavior is unchanged." +[[audits.der]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.8.1 -> 0.8.2" +notes = "Reviewed safe borrowing traits; nested reader restores state then rejects unconsumed input and marks failure. No unsafe or new I/O." + [[audits.dtor]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -42,6 +120,12 @@ criteria = "safe-to-deploy" delta = "0.0.6 -> 0.0.13" notes = "Reviewed delta: crate metadata only; generated code behavior is unchanged." +[[audits.either]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.17.0 -> 1.18.0" +notes = "Reviewed new Extend and FromIterator partitioning using safe iterators into caller collections. No unsafe or ambient capability changes." + [[audits.fancy-regex]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -53,6 +137,12 @@ who = "Mykhailo Chalyi " criteria = "safe-to-deploy" version = "2.4.0" +[[audits.find-msvc-tools]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.1.11 -> 0.1.12" +notes = "Reviewed existing Visual Studio discovery predicate rewrites with filter/and_then/then. Installation paths and architecture existence checks unchanged. No new process invocation or unsafe." + [[audits.get-size-derive2]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -71,22 +161,120 @@ criteria = "safe-to-deploy" version = "0.3.0" notes = "Reviewed complete crate: thin wasm-bindgen wrappers around host setTimeout/setInterval with RAII cancellation, plus futures channels. No Rust unsafe code, filesystem/network/process access, or attacker-controlled ambient capabilities; unchecked_ref casts wasm-bindgen Closure handles to the JavaScript Function type they represent." +[[audits.hermit-abi]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.5.2 -> 0.5.3" +notes = "Reviewed new unsafe extern sys_fsync declaration accepting explicit descriptor. Other changes docs/metadata; no automatic invocation or unchecked safe pointer wrapper." + [[audits.hybrid-array]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" version = "0.4.9" +[[audits.hyper]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.11.0 -> 1.11.1" +notes = "Reviewed multiple Connection close header handling, tokenized TE trailers, HTTP1 flush before yield, HTTP2 minimal capacity reservation after body chunk. Header terminator fast path matches parser acceptance; borrow changes preserve pointer/length invariants. No new destination." + +[[audits.icu_collator]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "2.3.0 -> 2.3.1" +notes = "Reviewed lookahead reset when restarting combining-character collection, preserving iterator state. No new unsafe or ambient capabilities." + +[[audits.icu_locale]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "2.3.0 -> 2.3.1" +notes = "Reviewed complete package delta: metadata/dependency versions only; locale source unchanged." + +[[audits.icu_provider]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "2.3.0 -> 2.3.1" +notes = "Reviewed trie cursor formatting early exit after failed prefix match. Safe formatting and optional lookup retained; no unsafe or ambient changes." + +[[audits.indexmap]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "2.14.0 -> 2.14.2" +notes = "Reviewed disjoint mutable references: all indices bounds-checked and duplicates excluded before references formed. Lifetimes preserved, centralized assertions, exact-key Eq and safe constructor insertions. No ambient changes." + [[audits.iri-string]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" version = "0.7.12" +[[audits.is-macro]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.3.7 -> 0.3.8" +notes = "Reviewed enum expect helpers optionally omitting Debug values in release panics. Matching and extraction unchanged, no unsafe or ambient access." + +[[audits.jaq-core]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "3.1.0 -> 3.1.1" +notes = "Reviewed complete package delta: dependency feature metadata only; interpreter source unchanged." + +[[audits.jaq-json]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "2.0.2 -> 2.0.3" +notes = "Reviewed complete package delta: dependency feature metadata only; JSON source unchanged." + +[[audits.jaq-std]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "3.0.2 -> 3.0.3" +notes = "Reviewed no_std float rounding with bounded IEEE754 bit operations; std keeps native floor/round/ceil. Corrected import feature gates; no unsafe or ambient changes." + +[[audits.jiter]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +version = "0.13.0" +importable = false +notes = "Local feature-scoped full source review: default/num-bigint only, with python forbidden by deny.toml (verified all-features graph and negative feature fixture). AArch64 loads require checked 16-byte slices, unchecked next-byte access remains inside the identified non-digit lane, and vector casts preserve initialized sizes. ASCII flags are cleared by non-ASCII bytes/Unicode escapes before unchecked UTF8; partial UTF8 uses validated prefix boundaries. Numeric loops progress and cap big integers at 4300 characters; value parsing limits nesting. No runtime filesystem/network/process access in reviewed features. EXCLUDED: optional Python FFI dereferences PyUnicode_New without checking allocation failure; this audit does not certify that feature. Re-audit before enabling it." + [[audits.jiter]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" delta = "0.13.0 -> 0.15.0" notes = "Reviewed delta: number parsing API refactor, NumberFloat export, Rust 2024 explicit unsafe blocks, and string-cache allocation moved off stack; no new filesystem/process/network capabilities, existing SIMD/PyString unsafe paths remain bounded by parser checks and ASCII preconditions." +[[audits.jiter]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.15.0 -> 0.15.0@git:6d57715e01ec78859c62fc5447073c0b5902de39" +importable = false +notes = "Reviewed complete crates.io 0.15.0 to exact pinned Git revision delta with cargo vet: only packaged README is removed. Rust source, manifests and build behavior unchanged. Policy explicitly requires published-base plus exact Git-delta audits." + +[[audits.js-sys]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.3.104 -> 0.3.105" +notes = "Reviewed JSPI task states preventing reentrant polling, Rc raw-waker balanced clone/wake/drop ownership and queued callbacks consuming owned reference. Future pinned and RefCell-protected; atomics excludes JSPI. Experimental JSPI requires matching generated glue and supported wasm32 runtime. No automatic new host destinations." + +[[audits.keccak]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.2.1 -> 0.2.2" +notes = "Reviewed precomputation of equivalent truncated round constants, last-round selection with const bound, and fixed loops replacing unroll macros. No new unsafe or ambient capability." + +[[audits.log]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.4.33 -> 0.4.34" +notes = "Reviewed boxed logger moving std to alloc, retains pointer-atomic gate and same one-time installation. No new unsafe or I/O paths." + +[[audits.mio]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.2.2 -> 1.2.3" +notes = "Reviewed shared registration state, Unix listener readiness rearming and BSD selector wakeups. kevent points to live event or null with zero output count; WASI fcntl uses owned socket. No new destinations or unchecked lengths." + [[audits.napi]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -105,12 +293,48 @@ criteria = "safe-to-deploy" delta = "5.0.2 -> 5.0.3" notes = "Reviewed delta: register_class call now forwards async iterator metadata; no new filesystem/process/network capabilities and generated N-API unsafe shape remains through existing napi abstractions." +[[audits.num-bigint]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.4.8 -> 0.5.1" +notes = "Reviewed unchanged arithmetic; rand .9/.10 adapters handle empty ranges fallibly. Byte fill length fits initialized digit allocation; zero-bit case avoids final-byte indexing and high-bit mask/endian conversion retained. No new ambient access." + +[[audits.ordermap]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.2.0 -> 1.2.2" +notes = "Reviewed const empty construction and safe insertion macros, indexing delegated to checked IndexMap. Other changes tests/diagnostics; no unsafe or ambient access." + +[[audits.owo-colors]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "4.3.0 -> 4.4.0" +notes = "Reviewed removal of compiler-probing build script, stabilized const mutable references with same bounded style bit positions. No new unsafe or I/O." + +[[audits.pageant]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.2.2 -> 0.2.3" +notes = "Reviewed shared-memory reads rejecting checked_add overflow and reads past mapping before copy_nonoverlapping. Errors propagate for length and payload; mapping ownership and agent destination unchanged." + +[[audits.portable-atomic-util]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.2.7 -> 0.2.8" +notes = "Reviewed Arc map uniqueness and equal size/alignment checks. Old T moves local and allocation becomes MaybeUninit U guard before closure, preserving allocation cleanup and preventing double-drop on panic. No ambient capability changes." + [[audits.rand]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" delta = "0.8.5 -> 0.8.6" notes = "Reviewed delta: backports RUSTSEC-2026-0097 fix by removing rand logging callbacks, panics on reseed failure instead of logging, removes experimental SIMD support, and keeps public RNG behavior otherwise compatible; no new ambient capabilities." +[[audits.rand]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.8.7 -> 0.8.8" +notes = "Reviewed complete delta: uses core primitive integer MAX paths to avoid removed associated module constants. RNG algorithms, sampling bounds and entropy sources unchanged." + [[audits.rand]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -134,18 +358,48 @@ criteria = "safe-to-deploy" delta = "0.62.5 -> 0.62.6" notes = "Reviewed delta: routes channel-open replies through a dedicated priority queue drained ahead of the bounded receivers, so a confirmation can no longer be silently dropped by a full queue or be overtaken by data for a channel that is not registered yet; enforces the server-side max_auth_attempts cap with a DISCONNECT while keeping publickey probes uncounted; and tolerates exactly one trailing comma in name-lists for OpenSSH interop, still rejecting a bare comma and a doubled separator. No new unsafe code (unchanged single occurrence), no new dependencies, and no ambient capability changes; each behaviour change lands with test coverage." +[[audits.russh]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.63.1 -> 0.63.2" +notes = "Reviewed credentials redaction/zeroization; negotiated RSA certificate hashes; rekey staging and peer non-transport flood rejection/bounds. Compression drains progress with existing maximum packet limit; PTY lengths count actual entries. No new unsafe or ambient destinations." + [[audits.rustls-webpki]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" delta = "0.103.12 -> 0.103.13" notes = "Reviewed delta: fixes BIT STRING/CRL parsing edge cases and preserves fail-closed URI constraint handling; no new unsafe code or ambient capability changes." +[[audits.rustls-webpki]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.103.14 -> 0.103.15" +notes = "Reviewed complete runtime delta: FIPS status reports underlying AWS-LC signature implementation status. Certificate parsing, verification algorithms and input bounds unchanged." + +[[audits.smallvec]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.15.2 -> 1.16.0" +notes = "Reviewed source formatting/import grouping and debugger visualizer removal. Storage ownership, bounds, raw pointer operations and panic guards unchanged; no new unsafe or ambient access." + +[[audits.syn]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "3.0.4 -> 3.0.5" +notes = "Reviewed parser diagnostic span adjustments; token consumption and syntax tree construction remain safe and bounded. No unsafe or ambient access." + [[audits.thin-vec]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" delta = "0.2.16 -> 0.2.18" notes = "Reviewed delta: no_std feature compatibility fixes and std-to-core path changes in gecko-ffi code; no new allocation semantics, unsafe behavior, or ambient capabilities." +[[audits.tinyvec]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.12.0 -> 1.13.2" +notes = "Reviewed formatting buffer changes: checked remaining-capacity copy and length update, safe array constructor. Initialized element and capacity bounds retained; no new ambient access." + [[audits.tokio]] who = "Mykhailo Chalyi " criteria = "safe-to-deploy" @@ -157,6 +411,102 @@ criteria = "safe-to-deploy" delta = "1.52.0 -> 1.52.1" notes = "Reviewed delta: reverts blocking-pool sharded queue to mutex/condvar VecDeque to fix spawn_blocking hang, adjusts fastrand cfg, and adds no new unsafe blocks or ambient capabilities." +[[audits.tokio-rustls]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.26.4 -> 0.26.5" +notes = "Reviewed bounded loop reading multiple immediately available TLS records, preserving buffer limits and wake/pending behavior. No new unsafe or network destination." + +[[audits.turso_core]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.8.0-pre.7 -> 0.8.0-pre.8" +notes = "Reviewed production delta: unchecked UTF8 requires all-ASCII aggregate; unique uninitialized Arc chunk assembly checks every index and complete size before assume_init. IO children link before submission, builder token prevents premature callbacks, SeqCst completion fires once with owned buffers. Transaction retry/abandonment retains write ownership and releases writer leases. Attached database sync is explicit; FTS storage remains in memory with descriptor bounds. No new host filesystem, network or process access. Source safety review, not formal transactional proof." + +[[audits.turso_ext]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.8.0-pre.7 -> 0.8.0-pre.8" +notes = "Reviewed complete package diff: version and turso_macros dependency version only. Extension source unchanged; no new runtime or ambient capability." + +[[audits.turso_macros]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.8.0-pre.7 -> 0.8.0-pre.8" +notes = "Reviewed complete package diff: only Cargo package version changes; macro source is byte-identical. No runtime, unsafe, host access, or build-script changes." + +[[audits.turso_parser]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.8.0-pre.7 -> 0.8.0-pre.8" +notes = "Reviewed named-token lexer: bounded get/peek accesses, progress on every branch, parenthesized suffix rejects missing delimiters and whitespace. New pragma/diagnostics use safe Rust; other parser changes are tests. No new unsafe or host capability." + +[[audits.twox-hash]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "2.1.3 -> 2.1.4" +notes = "Reviewed complete delta: formatting and private internal visibility changes, hash arithmetic and buffer reads unchanged. No new unsafe or host capability." + +[[audits.uuid]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "1.24.1 -> 1.26.0" +notes = "Reviewed serde adapter using existing UUID visitor and timestamp precision clamp to 12 bits. Existing random/clock source paths unchanged; no new unsafe or ambient access." + +[[audits.wasm-bindgen]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.2.127 -> 0.2.128" +notes = "Reviewed generic descriptor/ABI marker using live argument tuples and WasmRet with matching generated glue. String-like marker sealed to supported types; externref failure uses try_borrow. JSPI rejection state is single-threaded, requires matching schema glue. No new automatic host access." + +[[audits.wasm-bindgen-futures]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.4.77 -> 0.4.78" +notes = "Reviewed complete delta: reexports JSPI helpers from reviewed js-sys; existing scheduler source unchanged." + +[[audits.wasm-bindgen-macro]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.2.127 -> 0.2.128" +notes = "Reviewed complete delta: recognizes retried derive attribute; implementation remains in reviewed macro-support. No new host access." + +[[audits.wasm-bindgen-macro-support]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.2.127 -> 0.2.128" +notes = "Reviewed generic imports rejecting mutable/nested refs and return refs, preserving IntoWasmAbi/WasmDescribe and lifetime bounds. Marker points to live tuple, return ABI matches descriptor and noinline transformation boundary. Suspending results convert externref after resume; invalid attribute combinations rejected. Symbol hashes/cfg propagation prevent collisions. Requires matching runtime/glue schema; experimental JSPI supported wasm32 only." + +[[audits.wasm-bindgen-shared]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.2.127 -> 0.2.128" +notes = "Reviewed schema/hash update covering both fixed source schema files, matching JSPI/generic descriptors and crate-qualified symbols. Build reads fixed source and Cargo package metadata, no new dynamic destination." + +[[audits.web-sys]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.3.104 -> 0.3.105" +notes = "Reviewed generated shared input slices for read/copy browser APIs, typed feature-gated OffscreenCanvas WebGL catch overloads, and deprecation metadata. No automatic browser API invocation." + +[[audits.wnaf]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.14.0 -> 0.14.1" +notes = "Reviewed replacement of unconditional representation reversal by PrimeFieldExt to_le_repr with correct bounds. No unsafe or algorithm-loop changes, no new ambient access." + +[[audits.zerovec]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.11.7 -> 0.11.8" +notes = "Reviewed array validation rejecting zero-sized elements and invalid byte lengths before interpretation. Existing aligned/unaligned representation invariants retained; no ambient capability changes." + +[[audits.zerovec-derive]] +who = "Mykhailo Chalyi " +criteria = "safe-to-deploy" +delta = "0.11.4 -> 0.11.6" +notes = "Reviewed generated validator passing each element chunk instead of whole bytes; raw slice pointer construction avoids temporary out-of-bounds reference. Validation length constraints precede interpretation; no new host access." + [[trusted.serde_spanned]] criteria = "safe-to-deploy" user-id = 6743 # Ed Page (epage) diff --git a/supply-chain/config.toml b/supply-chain/config.toml index b2f9436d0..f0ef72fd7 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -27,6 +27,10 @@ criteria = "safe-to-deploy" audit-as-crates-io = false criteria = "safe-to-deploy" +[policy.jiter] +audit-as-crates-io = true +notes = "Require published-base AND exact Git-commit delta audits for the pinned third-party patch; https://mozilla.github.io/cargo-vet/first-party-code.html" + [[exemptions.adler2]] version = "2.0.1" criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 1db80dd20..905c27780 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -16,8 +16,8 @@ user-login = "Darksonn" user-name = "Alice Ryhl" [[publisher.toml]] -version = "1.1.4+spec-1.1.0" -when = "2026-07-28" +version = "1.1.5+spec-1.1.0" +when = "2026-09-02" user-id = 6743 user-login = "epage" user-name = "Ed Page"