diff --git a/.github/actions/ci-reviewed-npm-audit/action.yaml b/.github/actions/ci-reviewed-npm-audit/action.yaml index b85c339ee2b..019f956ccf2 100644 --- a/.github/actions/ci-reviewed-npm-audit/action.yaml +++ b/.github/actions/ci-reviewed-npm-audit/action.yaml @@ -2,7 +2,15 @@ # SPDX-License-Identifier: Apache-2.0 name: ci-reviewed-npm-audit -description: Materialize and audit the reviewed production npm graphs. +description: Audit a target tree's reviewed production npm graphs from trusted CI code. + +inputs: + target-root: + description: Absolute path to the checked-out target tree whose graph inputs are audited. + required: true + report-dir: + description: Report directory relative to target-root. + required: true runs: using: composite @@ -12,14 +20,28 @@ runs: with: node-version: "22.23.1" + - name: Pin production npm + shell: bash + run: >- + cd "$RUNNER_TEMP" && + npm install --global npm@10.9.4 + --userconfig /dev/null + --registry https://registry.npmjs.org/ + --ignore-scripts --no-audit --no-fund + - name: Materialize and audit reviewed npm graphs shell: bash - run: node --experimental-strip-types scripts/audit-reviewed-npm-graph.mts + env: + NEMOCLAW_REVIEWED_NPM_AUDIT_TARGET_ROOT: ${{ inputs.target-root }} + NEMOCLAW_REVIEWED_NPM_AUDIT_REPORT_DIR: ${{ inputs.report-dir }} + NPM_CONFIG_REGISTRY: https://registry.npmjs.org/ + NPM_CONFIG_USERCONFIG: /dev/null + run: node --experimental-strip-types "$GITHUB_ACTION_PATH/../../../scripts/audit-reviewed-npm-graph.mts" - name: Upload reviewed npm audit reports if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: reviewed-npm-audit - path: coverage/reviewed-npm-audit/*.json + path: ${{ inputs.target-root }}/${{ inputs.report-dir }}/*.json if-no-files-found: error diff --git a/.github/actions/resolve-sandbox-base-image/action.yaml b/.github/actions/resolve-sandbox-base-image/action.yaml index 5e3acc7e65d..0597bd93e9e 100644 --- a/.github/actions/resolve-sandbox-base-image/action.yaml +++ b/.github/actions/resolve-sandbox-base-image/action.yaml @@ -14,7 +14,13 @@ runs: image="ghcr.io/nvidia/nemoclaw/sandbox-base" min_glibc="2.39" - base_inputs=(Dockerfile.base nemoclaw-blueprint/blueprint.yaml) + base_inputs=( + Dockerfile.base + agents/openclaw/openclaw-runtime/package.json + agents/openclaw/openclaw-runtime/package-lock.json + nemoclaw-blueprint/blueprint.yaml + scripts/lib/reviewed-npm-archive.mts + ) source "${GITHUB_ACTION_PATH}/../base-image-resolver.sh" normalize_version_tag() { diff --git a/.github/workflows/base-image.yaml b/.github/workflows/base-image.yaml index 95422b0e9ea..241c0b3fe8e 100644 --- a/.github/workflows/base-image.yaml +++ b/.github/workflows/base-image.yaml @@ -23,6 +23,9 @@ on: # takes effect immediately after merge instead of waiting for another tag. - ".github/workflows/base-image.yaml" - "Dockerfile.base" + - "agents/openclaw/openclaw-runtime/package.json" + - "agents/openclaw/openclaw-runtime/package-lock.json" + - "scripts/lib/reviewed-npm-archive.mts" - "agents/hermes/Dockerfile.base" - "agents/langchain-deepagents-code/Dockerfile.base" - "agents/langchain-deepagents-code/manifest.yaml" @@ -34,7 +37,6 @@ on: - "nemoclaw-blueprint/blueprint.yaml" - "scripts/lib/openclaw-npm-remediation.mts" - "scripts/lib/reviewed-npm-audit.mts" - - "scripts/lib/reviewed-npm-archive.mts" - "scripts/checks/node-tar-image-scan.mts" - "scripts/patch-bundled-npm-tar.mts" - "scripts/upgrade-bundled-npm.mts" diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 90c7dd2370b..5c89ada5190 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -92,6 +92,9 @@ jobs: - name: Audit reviewed production npm graphs uses: ./.github/actions/ci-reviewed-npm-audit + with: + target-root: ${{ github.workspace }} + report-dir: artifacts/reviewed-npm-audit real-openclaw-dist-harness: runs-on: ubuntu-latest diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index a7ed35fdcf0..45ddc5103e5 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -285,8 +285,79 @@ jobs: with: persist-credentials: false + - name: Checkout trusted reviewed npm audit + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + ref: ${{ github.event.pull_request.base.sha }} + path: .trusted-reviewed-npm-audit + persist-credentials: false + sparse-checkout: | + .github/actions/ci-reviewed-npm-audit + ci/npm-audit-exceptions.json + ci/reviewed-npm-audit.json + scripts/audit-reviewed-npm-graph.mts + scripts/lib/openclaw-npm-remediation.mts + scripts/lib/reviewed-npm-archive.mts + scripts/lib/reviewed-npm-audit.mts + sparse-checkout-cone-mode: false + + - name: Detect trusted reviewed npm audit schema + id: trusted-reviewed-npm-audit + shell: bash + run: | + if grep -Fq 'NEMOCLAW_REVIEWED_NPM_AUDIT_TARGET_ROOT' \ + .trusted-reviewed-npm-audit/.github/actions/ci-reviewed-npm-audit/action.yaml \ + && grep -Fq 'resolveTrustedAuditConfigPath(TRUSTED_REPO_ROOT)' \ + .trusted-reviewed-npm-audit/scripts/audit-reviewed-npm-graph.mts \ + && [ -f .trusted-reviewed-npm-audit/ci/npm-audit-exceptions.json ] \ + && [ -f .trusted-reviewed-npm-audit/ci/reviewed-npm-audit.json ] \ + && [ -f .trusted-reviewed-npm-audit/scripts/lib/openclaw-npm-remediation.mts ] \ + && [ -f .trusted-reviewed-npm-audit/scripts/lib/reviewed-npm-audit.mts ]; then + echo "available=true" >> "$GITHUB_OUTPUT" + else + echo "available=false" >> "$GITHUB_OUTPUT" + fi + + # One-time bootstrap for this PR only. The action, driver, and helper are + # pinned to its signed first commit; every later PR must use its base SHA. + - name: Checkout pinned bootstrap reviewed npm audit + if: ${{ steps.trusted-reviewed-npm-audit.outputs.available != 'true' && github.event.pull_request.number == 6830 && github.event.pull_request.head.repo.full_name == 'HOYALIM/NemoClaw' }} + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + repository: HOYALIM/NemoClaw + ref: 0c7dd29394d2c4db660c4d09f3654c0789e200d0 + path: .trusted-reviewed-npm-audit-bootstrap + persist-credentials: false + sparse-checkout: | + .github/actions/ci-reviewed-npm-audit + ci/npm-audit-exceptions.json + ci/reviewed-npm-audit.json + scripts/audit-reviewed-npm-graph.mts + scripts/lib/openclaw-npm-remediation.mts + scripts/lib/reviewed-npm-archive.mts + scripts/lib/reviewed-npm-audit.mts + sparse-checkout-cone-mode: false + + - name: Reject unavailable trusted reviewed npm audit + if: ${{ steps.trusted-reviewed-npm-audit.outputs.available != 'true' && (github.event.pull_request.number != 6830 || github.event.pull_request.head.repo.full_name != 'HOYALIM/NemoClaw') }} + shell: bash + run: | + echo "::error::Trusted base SHA does not contain the schema-2 reviewed npm audit" + exit 1 + - name: Audit reviewed production npm graphs - uses: ./.github/actions/ci-reviewed-npm-audit + if: ${{ steps.trusted-reviewed-npm-audit.outputs.available == 'true' }} + uses: ./.trusted-reviewed-npm-audit/.github/actions/ci-reviewed-npm-audit + with: + target-root: ${{ github.workspace }} + report-dir: artifacts/reviewed-npm-audit + + - name: Audit reviewed production npm graphs (pinned bootstrap) + if: ${{ steps.trusted-reviewed-npm-audit.outputs.available != 'true' && github.event.pull_request.number == 6830 && github.event.pull_request.head.repo.full_name == 'HOYALIM/NemoClaw' }} + uses: ./.trusted-reviewed-npm-audit-bootstrap/.github/actions/ci-reviewed-npm-audit + with: + target-root: ${{ github.workspace }} + report-dir: artifacts/reviewed-npm-audit cli-test-shards: needs: changes diff --git a/Dockerfile b/Dockerfile index 47b4769c6d9..b7b66580c3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,6 +42,8 @@ RUN ln -s /opt/nemoclaw/node_modules /opt/nemoclaw-root/node_modules \ # related payloads without invalidating earlier final-image work. FROM scratch AS openclaw-dependency-payload +COPY agents/openclaw/openclaw-runtime/package.json /usr/local/lib/nemoclaw/openclaw-runtime/package.json +COPY agents/openclaw/openclaw-runtime/package-lock.json /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json COPY agents/openclaw/mcporter-runtime/package.json /usr/local/lib/nemoclaw/mcporter-runtime/package.json COPY agents/openclaw/mcporter-runtime/package-lock.json /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json COPY agents/openclaw/wechat-runtime/package.json /usr/local/lib/nemoclaw/wechat-runtime/package.json @@ -375,8 +377,17 @@ RUN set -eu; \ if [ -z "$EXPECTED_INTEGRITY" ]; then \ echo "ERROR: OpenClaw ${OPENCLAW_VERSION} has no committed npm integrity pin" >&2; exit 1; \ fi; \ + OPENCLAW_LOCK_SHA256=none-legacy-fixture; \ OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle-v1'; \ - if [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; fi; \ + if [ "$OPENCLAW_VERSION" = "2026.7.1" ]; then \ + OPENCLAW_LOCK_SHA256=82489f62febb12da52833c0b1f7f6969f7e21a098c565ef1f91342b1e5e32d88; \ + ACTUAL_OPENCLAW_LOCK_SHA256="$(sha256sum /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json | awk '{print $1}')"; \ + [ "$ACTUAL_OPENCLAW_LOCK_SHA256" = "$OPENCLAW_LOCK_SHA256" ] \ + || { echo "ERROR: OpenClaw lock SHA-256 mismatch (expected $OPENCLAW_LOCK_SHA256, found $ACTUAL_OPENCLAW_LOCK_SHA256)" >&2; exit 1; }; \ + OPENCLAW_RECIPE='locked-ci+reviewed-lifecycle-v2'; \ + elif [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then \ + OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; \ + fi; \ MCPORTER_EXPECTED_INTEGRITY=""; \ MCPORTER_EXPECTED_TARBALL=""; \ if [ "$MCPORTER_VERSION" = "0.7.3" ]; then MCPORTER_EXPECTED_INTEGRITY="$MCPORTER_0_7_3_INTEGRITY"; MCPORTER_EXPECTED_TARBALL="$MCPORTER_0_7_3_TARBALL"; fi; \ @@ -404,6 +415,7 @@ RUN set -eu; \ "package=openclaw@${OPENCLAW_VERSION}" \ "integrity=${EXPECTED_INTEGRITY}" \ "tarball=${EXPECTED_TARBALL}" \ + "lock-sha256=${OPENCLAW_LOCK_SHA256}" \ "recipe=${OPENCLAW_RECIPE}" \ "mcporter-package=mcporter@${MCPORTER_VERSION}" \ "mcporter-integrity=${MCPORTER_EXPECTED_INTEGRITY}" \ @@ -437,28 +449,49 @@ RUN set -eu; \ echo "ERROR: Base image has OpenClaw $CUR_VER, which is newer than reviewed target $OPENCLAW_VERSION" >&2; exit 1; \ else \ echo "INFO: Base image OpenClaw $CUR_VER lacks exact reviewed provenance; installing $OPENCLAW_VERSION"; \ - OPENCLAW_SOURCE_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \ - --package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \ - --tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \ - OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \ - OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_PACK_PATH")"; \ - if [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then \ - OPENCLAW_REMEDIATION_JSON="$(node --experimental-strip-types /scripts/lib/openclaw-npm-remediation.mts \ - --archive "$OPENCLAW_SOURCE_PACK_PATH" --package-spec "openclaw@${OPENCLAW_VERSION}" \ - --working-directory "$OPENCLAW_PACK_DIR")"; \ - OPENCLAW_PACK_PATH="$(node -e 'const value = JSON.parse(process.argv[1]); if (!value.remediated || typeof value.archivePath !== "string") process.exit(1); process.stdout.write(value.archivePath)' "$OPENCLAW_REMEDIATION_JSON")"; \ - fi; \ # npm 10's atomic-move install can hit EROFS on overlayfs when the prior # install spans image layers. Removing it first also prevents unreviewed # files from surviving a same-version reinstall. rm -rf /usr/local/lib/node_modules/openclaw /usr/local/bin/openclaw; \ - npm install -g --no-audit --no-fund --no-progress --ignore-scripts "$OPENCLAW_PACK_PATH"; \ - case "$OPENCLAW_VERSION" in \ - 2026.4.24|2026.7.1) node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs ;; \ - 2026.3.11) ;; \ - *) echo "ERROR: OpenClaw ${OPENCLAW_VERSION} has no reviewed lifecycle policy" >&2; exit 1 ;; \ - esac; \ - rm -rf "$OPENCLAW_PACK_DIR"; \ + if [ "$OPENCLAW_VERSION" = "2026.7.1" ]; then \ + node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts --verify-lock \ + --lock-sha256 "$OPENCLAW_LOCK_SHA256" \ + --lockfile /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json \ + --registry-origin https://registry.npmjs.org/ \ + --package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \ + --tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}"; \ + npm --prefix /usr/local/lib/nemoclaw/openclaw-runtime ci \ + --ignore-scripts --omit=dev --no-audit --no-fund --no-progress \ + --userconfig /dev/null --registry https://registry.npmjs.org/; \ + node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \ + --verify-installed-lock --lock-sha256 "$OPENCLAW_LOCK_SHA256" \ + --lockfile /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json \ + --install-root /usr/local/lib/nemoclaw/openclaw-runtime \ + --label "OpenClaw ${OPENCLAW_VERSION}"; \ + node /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs; \ + mkdir -p /usr/local/lib/node_modules; \ + ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw /usr/local/lib/node_modules/openclaw; \ + ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/.bin/openclaw /usr/local/bin/openclaw; \ + else \ + OPENCLAW_SOURCE_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \ + --package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \ + --tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \ + OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \ + OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_PACK_PATH")"; \ + if [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then \ + OPENCLAW_REMEDIATION_JSON="$(node --experimental-strip-types /scripts/lib/openclaw-npm-remediation.mts \ + --archive "$OPENCLAW_SOURCE_PACK_PATH" --package-spec "openclaw@${OPENCLAW_VERSION}" \ + --working-directory "$OPENCLAW_PACK_DIR")"; \ + OPENCLAW_PACK_PATH="$(node -e 'const value = JSON.parse(process.argv[1]); if (!value.remediated || typeof value.archivePath !== "string") process.exit(1); process.stdout.write(value.archivePath)' "$OPENCLAW_REMEDIATION_JSON")"; \ + fi; \ + npm install -g --no-audit --no-fund --no-progress --ignore-scripts "$OPENCLAW_PACK_PATH"; \ + case "$OPENCLAW_VERSION" in \ + 2026.4.24) node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs ;; \ + 2026.3.11) ;; \ + *) echo "ERROR: OpenClaw ${OPENCLAW_VERSION} has no reviewed lifecycle policy" >&2; exit 1 ;; \ + esac; \ + rm -rf "$OPENCLAW_PACK_DIR"; \ + fi; \ fi; \ case "$OPENCLAW_VERSION" in \ 2026.3.11) npm ls -g --depth=1 openclaw tar >/dev/null ;; \ diff --git a/Dockerfile.base b/Dockerfile.base index bc295089a3a..6da95e2bcff 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -378,6 +378,8 @@ ARG OPENCLAW_2026_4_24_TARBALL=https://registry.npmjs.org/openclaw/-/openclaw-20 ARG MCPORTER_VERSION=0.7.3 ARG MCPORTER_0_7_3_INTEGRITY=sha512-egoPVYqTnWb3NjRIxo+xc8OrAI0dlPrJm9pAiZx0pImuNIV5rKhGtTnIfH/Y1ldGPVu74ibj3KR5c9U/QSdQFA== ARG MCPORTER_0_7_3_TARBALL=https://registry.npmjs.org/mcporter/-/mcporter-0.7.3.tgz +COPY agents/openclaw/openclaw-runtime/package.json /usr/local/lib/nemoclaw/openclaw-runtime/package.json +COPY agents/openclaw/openclaw-runtime/package-lock.json /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json COPY agents/openclaw/mcporter-runtime/package.json /usr/local/lib/nemoclaw/mcporter-runtime/package.json COPY agents/openclaw/mcporter-runtime/package-lock.json /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json COPY ci/npm-audit-exceptions.json /scripts/npm-audit-exceptions.json @@ -438,30 +440,58 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep if [ -z "$EXPECTED_INTEGRITY" ]; then \ echo "Error: OpenClaw ${OPENCLAW_VERSION} has no committed npm integrity pin"; exit 1; \ fi; \ - OPENCLAW_SOURCE_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \ - --package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \ - --tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \ - if [ -z "$OPENCLAW_SOURCE_PACK_PATH" ] || [ ! -f "$OPENCLAW_SOURCE_PACK_PATH" ] || [ -L "$OPENCLAW_SOURCE_PACK_PATH" ]; then \ - echo "Error: reviewed OpenClaw archive path is empty or invalid"; exit 1; \ - fi; \ - OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \ - OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_PACK_PATH")"; \ + OPENCLAW_LOCK_SHA256=none-legacy-fixture; \ OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle-v1'; \ - if [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then \ - OPENCLAW_REMEDIATION_JSON="$(node --experimental-strip-types /scripts/lib/openclaw-npm-remediation.mts \ - --archive "$OPENCLAW_SOURCE_PACK_PATH" --package-spec "openclaw@${OPENCLAW_VERSION}" \ - --working-directory "$OPENCLAW_PACK_DIR")"; \ - OPENCLAW_PACK_PATH="$(node -e 'const value = JSON.parse(process.argv[1]); if (!value.remediated || typeof value.archivePath !== "string") process.exit(1); process.stdout.write(value.archivePath)' "$OPENCLAW_REMEDIATION_JSON")"; \ - OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; \ + if [ "$OPENCLAW_VERSION" = "2026.7.1" ]; then \ + OPENCLAW_LOCK_SHA256=82489f62febb12da52833c0b1f7f6969f7e21a098c565ef1f91342b1e5e32d88; \ + ACTUAL_OPENCLAW_LOCK_SHA256="$(sha256sum /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json | awk '{print $1}')"; \ + [ "$ACTUAL_OPENCLAW_LOCK_SHA256" = "$OPENCLAW_LOCK_SHA256" ] \ + || { echo "Error: OpenClaw lock SHA-256 mismatch (expected $OPENCLAW_LOCK_SHA256, found $ACTUAL_OPENCLAW_LOCK_SHA256)"; exit 1; }; \ + node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts --verify-lock \ + --lock-sha256 "$OPENCLAW_LOCK_SHA256" \ + --lockfile /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json \ + --registry-origin https://registry.npmjs.org/ \ + --package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \ + --tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}"; \ + rm -rf /usr/local/lib/node_modules/openclaw /usr/local/bin/openclaw; \ + npm --prefix /usr/local/lib/nemoclaw/openclaw-runtime ci \ + --ignore-scripts --omit=dev --no-audit --no-fund --no-progress \ + --userconfig /dev/null --registry https://registry.npmjs.org/; \ + node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \ + --verify-installed-lock --lock-sha256 "$OPENCLAW_LOCK_SHA256" \ + --lockfile /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json \ + --install-root /usr/local/lib/nemoclaw/openclaw-runtime \ + --label "OpenClaw ${OPENCLAW_VERSION}"; \ + node /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs; \ + mkdir -p /usr/local/lib/node_modules; \ + ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw /usr/local/lib/node_modules/openclaw; \ + ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/.bin/openclaw /usr/local/bin/openclaw; \ + OPENCLAW_RECIPE='locked-ci+reviewed-lifecycle-v2'; \ + else \ + OPENCLAW_SOURCE_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \ + --package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \ + --tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \ + if [ -z "$OPENCLAW_SOURCE_PACK_PATH" ] || [ ! -f "$OPENCLAW_SOURCE_PACK_PATH" ] || [ -L "$OPENCLAW_SOURCE_PACK_PATH" ]; then \ + echo "Error: reviewed OpenClaw archive path is empty or invalid"; exit 1; \ + fi; \ + OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \ + OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_PACK_PATH")"; \ + if [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then \ + OPENCLAW_REMEDIATION_JSON="$(node --experimental-strip-types /scripts/lib/openclaw-npm-remediation.mts \ + --archive "$OPENCLAW_SOURCE_PACK_PATH" --package-spec "openclaw@${OPENCLAW_VERSION}" \ + --working-directory "$OPENCLAW_PACK_DIR")"; \ + OPENCLAW_PACK_PATH="$(node -e 'const value = JSON.parse(process.argv[1]); if (!value.remediated || typeof value.archivePath !== "string") process.exit(1); process.stdout.write(value.archivePath)' "$OPENCLAW_REMEDIATION_JSON")"; \ + OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; \ + fi; \ + npm install -g --ignore-scripts "$OPENCLAW_PACK_PATH"; \ + case "$OPENCLAW_VERSION" in \ + 2026.4.24) node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs ;; \ + 2026.3.11) ;; \ + *) echo "Error: OpenClaw ${OPENCLAW_VERSION} has no reviewed lifecycle policy"; exit 1 ;; \ + esac; \ + rm -rf "$OPENCLAW_PACK_DIR"; \ fi; \ - npm install -g --ignore-scripts "$OPENCLAW_PACK_PATH" \ - && case "$OPENCLAW_VERSION" in \ - 2026.4.24|2026.7.1) node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs ;; \ - 2026.3.11) ;; \ - *) echo "Error: OpenClaw ${OPENCLAW_VERSION} has no reviewed lifecycle policy"; exit 1 ;; \ - esac \ - && rm -rf "$OPENCLAW_PACK_DIR" \ - && OPENCLAW_INSTALLED_VERSION="$(openclaw --version 2>/dev/null | awk '{print $2}')" \ + OPENCLAW_INSTALLED_VERSION="$(openclaw --version 2>/dev/null | awk '{print $2}')" \ && if [ "$OPENCLAW_INSTALLED_VERSION" != "$OPENCLAW_VERSION" ]; then \ echo "Error: Installed OpenClaw ${OPENCLAW_INSTALLED_VERSION:-unknown} does not match reviewed target ${OPENCLAW_VERSION}"; exit 1; \ fi \ @@ -506,6 +536,7 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep "package=openclaw@${OPENCLAW_VERSION}" \ "integrity=${EXPECTED_INTEGRITY}" \ "tarball=${EXPECTED_TARBALL}" \ + "lock-sha256=${OPENCLAW_LOCK_SHA256}" \ "recipe=${OPENCLAW_RECIPE}" \ "mcporter-package=mcporter@${MCPORTER_VERSION}" \ "mcporter-integrity=${MCPORTER_EXPECTED_INTEGRITY}" \ diff --git a/agents/openclaw/openclaw-runtime/package-lock.json b/agents/openclaw/openclaw-runtime/package-lock.json new file mode 100644 index 00000000000..7cb2f69b89f --- /dev/null +++ b/agents/openclaw/openclaw-runtime/package-lock.json @@ -0,0 +1,3648 @@ +{ + "name": "nemoclaw-openclaw-runtime", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nemoclaw-openclaw-runtime", + "version": "0.0.0", + "license": "Apache-2.0", + "dependencies": { + "openclaw": "2026.7.1" + }, + "engines": { + "node": ">=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0" + } + }, + "node_modules/openclaw": { + "version": "2026.7.1", + "resolved": "https://registry.npmjs.org/openclaw/-/openclaw-2026.7.1.tgz", + "integrity": "sha512-ge/Xss99CHAjPL/ikmH/UFoiOrjcxDB4sW3y9mhyCD+dYW3wzV7TKbAVdkrXFgAG2d2BjpJofP97zUZ+umxo8g==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@agentclientprotocol/sdk": "1.1.0", + "@anthropic-ai/sdk": "0.109.1", + "@clack/core": "1.4.2", + "@clack/prompts": "1.6.0", + "@earendil-works/pi-tui": "0.80.3", + "@google/genai": "2.10.0", + "@grammyjs/runner": "2.0.3", + "@grammyjs/transformer-throttler": "1.2.1", + "@homebridge/ciao": "1.3.9", + "@lydell/node-pty": "1.2.0-beta.12", + "@mistralai/mistralai": "2.4.0", + "@modelcontextprotocol/sdk": "1.29.0", + "@mozilla/readability": "0.6.0", + "@openclaw/ai": "2026.7.1", + "@openclaw/fs-safe": "0.4.1", + "@openclaw/proxyline": "0.3.3", + "@silvia-odwyer/photon-node": "0.3.4", + "chalk": "5.6.2", + "chokidar": "5.0.0", + "clawpdf": "0.3.0", + "commander": "15.0.0", + "croner": "10.0.1", + "diff": "9.0.0", + "dotenv": "17.4.2", + "express": "5.2.1", + "file-type": "22.0.1", + "glob": "13.0.6", + "grammy": "1.44.0", + "highlight.js": "11.11.1", + "hosted-git-info": "10.1.1", + "ignore": "7.0.5", + "jiti": "2.7.0", + "json5": "2.2.3", + "jszip": "3.10.1", + "kysely": "0.29.2", + "linkedom": "0.18.12", + "minimatch": "10.2.5", + "node-edge-tts": "1.2.10", + "openai": "6.45.0", + "partial-json": "0.1.7", + "playwright-core": "1.61.1", + "proper-lockfile": "4.1.2", + "qrcode": "1.5.4", + "quickjs-wasi": "3.0.2", + "rastermill": "0.3.1", + "tar": "7.5.19", + "tree-sitter-bash": "0.25.1", + "tslog": "4.10.2", + "typebox": "1.3.3", + "typescript": "6.0.3", + "undici": "8.5.0", + "web-push": "3.6.7", + "web-tree-sitter": "0.26.10", + "ws": "8.21.0", + "yaml": "2.9.0", + "zod": "4.4.3" + }, + "bin": { + "openclaw": "openclaw.mjs" + }, + "engines": { + "node": ">=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0" + }, + "optionalDependencies": { + "sqlite-vec": "0.1.9" + } + }, + "node_modules/openclaw/node_modules/@agentclientprotocol/sdk": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.1.0.tgz", + "integrity": "sha512-NT2KqphUJ3w6EksUL51ZhJgIYgq/ZLGcBPkyMKgRSO5PMVwe9DnKKX+Htnvk6KHh6dUuh34UHK4gKp+4te1Mdg==", + "license": "Apache-2.0", + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/openclaw/node_modules/@anthropic-ai/sdk": { + "version": "0.109.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.109.1.tgz", + "integrity": "sha512-q9OnEKLr5H9nxSuXdgDgJhxfYMiE+AaUEBze2Gk91UcaaLnsN+Lx5fbCYywiqurU/APLdwv23x03Wm6WN3EBsg==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/openclaw/node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/openclaw/node_modules/@clack/core": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.2.tgz", + "integrity": "sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/openclaw/node_modules/@clack/prompts": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.6.0.tgz", + "integrity": "sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.2", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/openclaw/node_modules/@earendil-works/pi-tui": { + "version": "0.80.3", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.3.tgz", + "integrity": "sha512-2BJI6qwRQfnM0Q7seL1+SbacU/jRRjBnN7Hu3n9BjAn7/s5FaBNnvdD1qBQYRsFTHfjqMaDsjYqanPyqwXj99w==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "1.6.0", + "marked": "18.0.5" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/openclaw/node_modules/@google/genai": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.10.0.tgz", + "integrity": "sha512-e4cFxj3tiuMtsgOT4G9c1hXyGJhg7/Buj7VVeBacRY3fRtkRZZ59Q3nuVp2xbq8BGQXLXCDB253qMhklMOeUDg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/@grammyjs/runner": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@grammyjs/runner/-/runner-2.0.3.tgz", + "integrity": "sha512-nckmTs1dPWfVQteK9cxqxzE+0m1VRvluLWB8UgFzsjg62w3qthPJt0TYtJBEdG7OedvfQq4vnFAyE6iaMkR42A==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0" + }, + "engines": { + "node": ">=12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.13.1" + } + }, + "node_modules/openclaw/node_modules/@grammyjs/transformer-throttler": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@grammyjs/transformer-throttler/-/transformer-throttler-1.2.1.tgz", + "integrity": "sha512-CpWB0F3rJdUiKsq7826QhQsxbZi4wqfz1ccKX+fr+AOC+o8K7ZvS+wqX0suSu1QCsyUq2MDpNiKhyL2ZOJUS4w==", + "license": "MIT", + "dependencies": { + "bottleneck": "^2.0.0" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.0.0" + } + }, + "node_modules/openclaw/node_modules/@grammyjs/types": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/@grammyjs/types/-/types-3.28.0.tgz", + "integrity": "sha512-4JvXCdxRZHCje0M4gHzLwtB4bLno3WD28xd8CNfk4POWIu73BFnSvGeW6OQ5gPem4eYTEwkD9yDaXssixl6tMQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/@homebridge/ciao": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.9.tgz", + "integrity": "sha512-TMy9zy173jDOpnFXDqL3BPIQn5lfcAkSsivYQatCCakoHk4fLGd7QjfAaNGYE3Ox+/ZI6Lq0e1gGcz1qdw/IbA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "fast-deep-equal": "^3.1.3", + "source-map-support": "^0.5.21", + "tslib": "^2.8.1" + }, + "bin": { + "ciao-bcs": "lib/bonjour-conformance-testing.js" + } + }, + "node_modules/openclaw/node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/openclaw/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/openclaw/node_modules/@lydell/node-pty": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty/-/node-pty-1.2.0-beta.12.tgz", + "integrity": "sha512-qIK890UwPupoj07osVvgOIa++1mxeHbcGry4PKRHhNVNs81V2SCG34eJr46GybiOmBtc8Sj5PB1/GGM5PL549g==", + "license": "MIT", + "optionalDependencies": { + "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", + "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", + "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", + "@lydell/node-pty-linux-x64": "1.2.0-beta.12", + "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", + "@lydell/node-pty-win32-x64": "1.2.0-beta.12" + } + }, + "node_modules/openclaw/node_modules/@lydell/node-pty-darwin-arm64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-arm64/-/node-pty-darwin-arm64-1.2.0-beta.12.tgz", + "integrity": "sha512-tqaifcY9Cr41SblO1+FLzh8oxxtkNhuW9Dhl22lKme9BreYvKvxEZcdPIXTuqkJc5tagOEC4QHShKmJjLyLXLQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/openclaw/node_modules/@lydell/node-pty-darwin-x64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-x64/-/node-pty-darwin-x64-1.2.0-beta.12.tgz", + "integrity": "sha512-4LrS5pCJwqHKDVf1zS2gyNV0m4hKAXch+XZNhbZ6LY8uwVL8BhchzQBO40Os5anuRxRCWzHpw4Sp64Ie8q7E4Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/openclaw/node_modules/@lydell/node-pty-linux-arm64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-arm64/-/node-pty-linux-arm64-1.2.0-beta.12.tgz", + "integrity": "sha512-Sx+A71x5BDGHt9ansfrtGxwq2VFVDWvJUAdlUL0Hv0qeiJUfts+hgopx+CgT4PSwahKjdEgtu0+FAfY9rICKRw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/openclaw/node_modules/@lydell/node-pty-linux-x64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-x64/-/node-pty-linux-x64-1.2.0-beta.12.tgz", + "integrity": "sha512-bJzs94njofYhGg/UDqW1nj0dtvvu+2OvxMY+RlLS1T17VgcktKoIR6PuenTwE5HJ/D6StCPADmXcT0nNsCKmIQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/openclaw/node_modules/@lydell/node-pty-win32-arm64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-arm64/-/node-pty-win32-arm64-1.2.0-beta.12.tgz", + "integrity": "sha512-p7POgjVEiFaBC3/y+AKuV1FzePCsJ6HmZDv2XK+jBZSfwP8+uBAw181ZiKYN1YuRa/XpmBGaWezcI8hZkbW++g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/openclaw/node_modules/@lydell/node-pty-win32-x64": { + "version": "1.2.0-beta.12", + "resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-x64/-/node-pty-win32-x64-1.2.0-beta.12.tgz", + "integrity": "sha512-IDFa00g7qUDGUYgByrUBJtC+mOjYVt/8KYyWivCg5JjGOHbBUACUQZLl0jTWmnr+tld/UyTpX90a2PY6oTVtRw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/openclaw/node_modules/@mistralai/mistralai": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.4.0.tgz", + "integrity": "sha512-t6hCx242MTGolB76CI+17jDtPIe/bzLsMdUTMMoMn9Qo1h02N2G5jQYHmKDGU3X//OgR2wvngTD7tO6tPp5poQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.40.0", + "ws": "^8.18.0", + "zod": "^3.25.0 || ^4.0.0", + "zod-to-json-schema": "^3.25.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/openclaw/node_modules/@mozilla/readability": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz", + "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/openclaw/node_modules/@openclaw/ai": { + "version": "2026.7.1", + "resolved": "https://registry.npmjs.org/@openclaw/ai/-/ai-2026.7.1.tgz", + "integrity": "sha512-FsKy5DXSHf4qyN8Huoz/10HZRgoEwLF4uk8UWaCafaIler+q5Fsl51HcrIqIrEe0S38OT7LOaxnR++MOshAlmw==", + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "0.109.1", + "@google/genai": "2.10.0", + "@mistralai/mistralai": "2.4.0", + "openai": "6.45.0", + "partial-json": "0.1.7", + "typebox": "1.3.3" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/openclaw/node_modules/@openclaw/fs-safe": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@openclaw/fs-safe/-/fs-safe-0.4.1.tgz", + "integrity": "sha512-hQi+BxO10KdRFlYUot1syC+hTaUnGeQNdqX5kwkKJig8CFq1tKsYJLPm+zkiiGsSKOprPAquQl/txejEhpKPgg==", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "optionalDependencies": { + "jszip": "^3.10.1", + "tar": "7.5.19" + } + }, + "node_modules/openclaw/node_modules/@openclaw/proxyline": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@openclaw/proxyline/-/proxyline-0.3.3.tgz", + "integrity": "sha512-sftHnW69NHQqLjCxBTvQ8f/eQl+peZ5pHCBQtuTWBbeuYRHZ0/GXVTmw/O/YKsShMbqPWhJB0UYtPPdvCUSS8w==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + }, + "peerDependencies": { + "undici": ">=8.3.0 <9" + } + }, + "node_modules/openclaw/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", + "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/openclaw/node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/openclaw/node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@protobufjs/inquire": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/@silvia-odwyer/photon-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", + "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", + "license": "Apache-2.0" + }, + "node_modules/openclaw/node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/openclaw/node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/@types/node": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", + "integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/openclaw/node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/openclaw/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/openclaw/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/openclaw/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openclaw/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/openclaw/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/openclaw/node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/openclaw/node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/openclaw/node_modules/bn.js": { + "version": "4.12.4", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.4.tgz", + "integrity": "sha512-njR1b+ixG2ufvL9Zn9JGneW+b5GV6jqpYyPPpg4QVt723b5kJPGUczkUyWEH9BwEA74UakJZ43I4FDLBF7ci0g==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/openclaw/node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/openclaw/node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/openclaw/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/openclaw/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/openclaw/node_modules/clawpdf": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/clawpdf/-/clawpdf-0.3.0.tgz", + "integrity": "sha512-41+3AnKk9yek2sm+/9XvUlDTN8Wi+ag7fmxZuqw+ySn4lqaf/fCgLeamqPLiXY4gVbizKEHGoTG/JrIIFNE2rw==", + "license": "MIT", + "bin": { + "clawpdf": "dist/cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/openclaw/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/openclaw/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/openclaw/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/openclaw/node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/openclaw/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/openclaw/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/openclaw/node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/croner": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/croner/-/croner-10.0.1.tgz", + "integrity": "sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==", + "funding": [ + { + "type": "other", + "url": "https://paypal.me/hexagonpp" + }, + { + "type": "github", + "url": "https://github.com/sponsors/hexagon" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/openclaw/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/openclaw/node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/openclaw/node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/openclaw/node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/openclaw/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/openclaw/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/openclaw/node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/openclaw/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/openclaw/node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/openclaw/node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/openclaw/node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/openclaw/node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/openclaw/node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/openclaw/node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/openclaw/node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/openclaw/node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/openclaw/node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/openclaw/node_modules/eventsource-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", + "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/openclaw/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/express-rate-limit": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/openclaw/node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, + "node_modules/openclaw/node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/openclaw/node_modules/fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/openclaw/node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/openclaw/node_modules/file-type": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-22.0.1.tgz", + "integrity": "sha512-ww5Mhre0EE+jmBvOXTmXAbEMuZE7uX4a3+oRCQFNj8w++g3ev913N6tXQz0XTXbueQ5TWQfm6BdaViEHHn8bhA==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.5", + "token-types": "^6.1.2", + "uint8array-extras": "^1.5.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/openclaw/node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/openclaw/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/gaxios": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.5.tgz", + "integrity": "sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/openclaw/node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/openclaw/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/openclaw/node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openclaw/node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/openclaw/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/openclaw/node_modules/google-auth-library": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.9.0.tgz", + "integrity": "sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/openclaw/node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/openclaw/node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/grammy": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/grammy/-/grammy-1.44.0.tgz", + "integrity": "sha512-gGVykS5+c5f1tPV97LuU6IDRMawE2NzpwM9pNz58HQ35IZXDnYL3VOLvNzYognPSeBIOSzQXRu5w96V0aY8y8A==", + "license": "MIT", + "dependencies": { + "@grammyjs/types": "3.28.0", + "abort-controller": "^3.0.0", + "debug": "^4.4.3", + "node-fetch": "^2.7.0" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + } + }, + "node_modules/openclaw/node_modules/grammy/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/openclaw/node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/openclaw/node_modules/hono": { + "version": "4.12.25", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.25.tgz", + "integrity": "sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/openclaw/node_modules/hosted-git-info": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-10.1.1.tgz", + "integrity": "sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==", + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/openclaw/node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/openclaw/node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/http_ece": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz", + "integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/openclaw/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/openclaw/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/openclaw/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/openclaw/node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/openclaw/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/openclaw/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/openclaw/node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openclaw/node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/openclaw/node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/openclaw/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/openclaw/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/openclaw/node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/openclaw/node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/openclaw/node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/openclaw/node_modules/kysely": { + "version": "0.29.2", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.29.2.tgz", + "integrity": "sha512-s6WVJyEZrbm6jhBpiKHsGHyePMrVQKJ85wZCFCr9W4QHv6WTjWIrdvTmO9hDEA3bNK0xkrE2DqrHsXMLWuZpQg==", + "license": "MIT", + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/openclaw/node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/openclaw/node_modules/linkedom": { + "version": "0.18.12", + "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz", + "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==", + "license": "ISC", + "dependencies": { + "css-select": "^5.1.0", + "cssom": "^0.5.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^10.0.0", + "uhyphen": "^0.2.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "canvas": ">= 2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/openclaw/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/openclaw/node_modules/marked": { + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", + "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/openclaw/node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/openclaw/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openclaw/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/openclaw/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/openclaw/node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/openclaw/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/openclaw/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/openclaw/node_modules/node-addon-api": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.0.tgz", + "integrity": "sha512-ekZMeaaIzSQTSpr7X2X3iJM7lTzgnx8ahAG9pJfT/7+14mlEM8ZYQ9cgCDvSSRbReFK0oHli3WrZdCiRsgAT9Q==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/openclaw/node_modules/node-domexception": { + "name": "@nolyfill/domexception", + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/@nolyfill/domexception/-/domexception-1.0.28.tgz", + "integrity": "sha512-tlc/FcYIv5i8RYsl2iDil4A0gOihaas1R5jPcIC4Zw3GhjKsVilw90aHcVlhZPTBLGBzd379S+VcnsDjd9ChiA==", + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/openclaw/node_modules/node-edge-tts": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/node-edge-tts/-/node-edge-tts-1.2.10.tgz", + "integrity": "sha512-bV2i4XU54D45+US0Zm1HcJRkifuB3W438dWyuJEHLQdKxnuqlI1kim2MOvR6Q3XUQZvfF9PoDyR1Rt7aeXhPdQ==", + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^7.0.1", + "ws": "^8.13.0", + "yargs": "^17.7.2" + }, + "bin": { + "node-edge-tts": "bin.js" + } + }, + "node_modules/openclaw/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/openclaw/node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/openclaw/node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/openclaw/node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openclaw/node_modules/openai": { + "version": "6.45.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.45.0.tgz", + "integrity": "sha512-5DQVNErssk0afNpTTHUm/qZPU4iKR9OYdNid8Ib4puq4gHNNvGWZht2zY4h9a8JMF949Ik6m8gQutllVPbjdnw==", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/credential-provider-node": ">=3.972.0 <4", + "@smithy/hash-node": ">=4.3.0 <5", + "@smithy/signature-v4": ">=5.4.0 <6", + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-provider-node": { + "optional": true + }, + "@smithy/hash-node": { + "optional": true + }, + "@smithy/signature-v4": { + "optional": true + }, + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openclaw/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/p-retry/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/openclaw/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/openclaw/node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/openclaw/node_modules/path-to-regexp": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.0.tgz", + "integrity": "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/openclaw/node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/openclaw/node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/openclaw/node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/openclaw/node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/openclaw/node_modules/protobufjs": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.3.tgz", + "integrity": "sha512-+k0vdJKNdW+Vu+dYe8tZA/VvQb6XKNWexC6URwBFXxNnjLJz9nQJCemGyNgRAWD+B7+nGNc9qMPGwcD7s4nzUw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/openclaw/node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/openclaw/node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/openclaw/node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/openclaw/node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/openclaw/node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/quickjs-wasi": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-3.0.2.tgz", + "integrity": "sha512-SyfPzlrfz67/kv0SogmQgW4c2I1klkLcbvj9Y2gc1h7+VylmvuGevFljLXibGKajKJKiJV29d4S6FQLA6Sc80A==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/rastermill": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/rastermill/-/rastermill-0.3.1.tgz", + "integrity": "sha512-CX4nij6+ZLHYIaojJNfLTr7W+AiH/IPJi6E9Aw1br2///1KZL2KBOHd68rkcLedc47MPvb4hhH+fzYeGFa4A/Q==", + "license": "MIT", + "dependencies": { + "@silvia-odwyer/photon-node": "0.3.4" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/openclaw/node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/openclaw/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/openclaw/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/openclaw/node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/openclaw/node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/openclaw/node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/openclaw/node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/openclaw/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/openclaw/node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/openclaw/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/openclaw/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/openclaw/node_modules/sqlite-vec": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec/-/sqlite-vec-0.1.9.tgz", + "integrity": "sha512-L7XJWRIBNvR9O5+vh1FQ+IGkh/3D2AzVksW5gdtk28m78Hy8skFD0pqReKH1Yp0/BUKRGcffgKvyO/EON5JXpA==", + "license": "MIT OR Apache", + "optional": true, + "optionalDependencies": { + "sqlite-vec-darwin-arm64": "0.1.9", + "sqlite-vec-darwin-x64": "0.1.9", + "sqlite-vec-linux-arm64": "0.1.9", + "sqlite-vec-linux-x64": "0.1.9", + "sqlite-vec-windows-x64": "0.1.9" + } + }, + "node_modules/openclaw/node_modules/sqlite-vec-darwin-arm64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-darwin-arm64/-/sqlite-vec-darwin-arm64-0.1.9.tgz", + "integrity": "sha512-jSsZpE42OfBkGL/ItyJTVCUwl6o6Ka3U5rc4j+UBDIQzC1ulSSKMEhQLthsOnF/MdAf1MuAkYhkdKmmcjaIZQg==", + "cpu": [ + "arm64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/openclaw/node_modules/sqlite-vec-darwin-x64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-darwin-x64/-/sqlite-vec-darwin-x64-0.1.9.tgz", + "integrity": "sha512-KDlVyqQT7pnOhU1ymB9gs7dMbSoVmKHitT+k1/xkjarcX8bBqPxWrGlK/R+C5WmWkfvWwyq5FfXfiBYCBs6PlA==", + "cpu": [ + "x64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/openclaw/node_modules/sqlite-vec-linux-arm64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-linux-arm64/-/sqlite-vec-linux-arm64-0.1.9.tgz", + "integrity": "sha512-5wXVJ9c9kR4CHm/wVqXb/R+XUHTdpZ4nWbPHlS+gc9qQFVHs92Km4bPnCKX4rtcPMzvNis+SIzMJR1SCEwpuUw==", + "cpu": [ + "arm64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/openclaw/node_modules/sqlite-vec-linux-x64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-linux-x64/-/sqlite-vec-linux-x64-0.1.9.tgz", + "integrity": "sha512-w3tCH8xK2finW8fQJ/m8uqKodXUZ9KAuAar2UIhz4BHILfpE0WM/MTGCRfa7RjYbrYim5Luk3guvMOGI7T7JQA==", + "cpu": [ + "x64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/openclaw/node_modules/sqlite-vec-windows-x64": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/sqlite-vec-windows-x64/-/sqlite-vec-windows-x64-0.1.9.tgz", + "integrity": "sha512-y3gEIyy/17bq2QFPQOWLE68TYWcRZkBQVA2XLrTPHNTOp55xJi/BBBmOm40tVMDMjtP+Elpk6UBUXdaq+46b0Q==", + "cpu": [ + "x64" + ], + "license": "MIT OR Apache", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/openclaw/node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, + "node_modules/openclaw/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/openclaw/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openclaw/node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/openclaw/node_modules/tar": { + "version": "7.5.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz", + "integrity": "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/openclaw/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/openclaw/node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/openclaw/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/tree-sitter-bash": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/tree-sitter-bash/-/tree-sitter-bash-0.25.1.tgz", + "integrity": "sha512-7hMytuYIMoXOq24yRulgIxthE9YmggZIOHCyPTTuJcu6EU54tYD+4G39cUb28kxC6jMf/AbPfWGLQtgPTdh3xw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.2.1", + "node-gyp-build": "^4.8.2" + }, + "peerDependencies": { + "tree-sitter": "^0.25.0" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/openclaw/node_modules/tslog": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.10.2.tgz", + "integrity": "sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/fullstack-build/tslog?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/openclaw/node_modules/typebox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.3.tgz", + "integrity": "sha512-URXGUE31PJDQC+PtRMJeLdF4kmmOdFoVPikPCtV2oOIhUpNpppEdIz7W8bH8cFYPYHdDpaRvqwdegMTmHliudg==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/openclaw/node_modules/uhyphen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", + "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openclaw/node_modules/undici": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", + "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/openclaw/node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/openclaw/node_modules/web-push": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz", + "integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==", + "license": "MPL-2.0", + "dependencies": { + "asn1.js": "^5.3.0", + "http_ece": "1.2.0", + "https-proxy-agent": "^7.0.0", + "jws": "^4.0.0", + "minimist": "^1.2.5" + }, + "bin": { + "web-push": "src/cli.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/openclaw/node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/openclaw/node_modules/web-tree-sitter": { + "version": "0.26.10", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.26.10.tgz", + "integrity": "sha512-vengBGYS7FpAerkR3o04oBL4L8MkVmjawK50AFBu7v0HZBkmF9ZavPGKoXLSSmRhp7T/YgsJ7joAS3yAxHPEqQ==", + "license": "MIT" + }, + "node_modules/openclaw/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/openclaw/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/openclaw/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/openclaw/node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/openclaw/node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/openclaw/node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/openclaw/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/openclaw/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/openclaw/node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/openclaw/node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/openclaw/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/openclaw/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/openclaw/node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/agents/openclaw/openclaw-runtime/package.json b/agents/openclaw/openclaw-runtime/package.json new file mode 100644 index 00000000000..d651034e53a --- /dev/null +++ b/agents/openclaw/openclaw-runtime/package.json @@ -0,0 +1,14 @@ +{ + "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0", + "name": "nemoclaw-openclaw-runtime", + "version": "0.0.0", + "private": true, + "description": "Locked production dependency graph for the OpenClaw runtime", + "license": "Apache-2.0", + "dependencies": { + "openclaw": "2026.7.1" + }, + "engines": { + "node": ">=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0" + } +} diff --git a/ci/reviewed-npm-audit.json b/ci/reviewed-npm-audit.json index d6ccb358d38..d57d87bc81e 100644 --- a/ci/reviewed-npm-audit.json +++ b/ci/reviewed-npm-audit.json @@ -1,6 +1,7 @@ { "schemaVersion": 2, "nodeVersion": "22.23.1", + "registryOrigin": "https://registry.npmjs.org/", "severityThreshold": "high", "exceptionFile": "ci/npm-audit-exceptions.json", "archiveGraphId": "reviewed-archive-graph", @@ -62,13 +63,23 @@ } ], "lockedGraphs": [ + { + "id": "openclaw-runtime", + "label": "OpenClaw 2026.7.1 locked runtime graph", + "packageSpec": "openclaw@2026.7.1", + "integrity": "sha512-ge/Xss99CHAjPL/ikmH/UFoiOrjcxDB4sW3y9mhyCD+dYW3wzV7TKbAVdkrXFgAG2d2BjpJofP97zUZ+umxo8g==", + "tarballUrl": "https://registry.npmjs.org/openclaw/-/openclaw-2026.7.1.tgz", + "directory": "agents/openclaw/openclaw-runtime", + "lockSha256": "82489f62febb12da52833c0b1f7f6969f7e21a098c565ef1f91342b1e5e32d88" + }, { "id": "mcporter-runtime", "label": "mcporter 0.7.3 locked runtime graph", "packageSpec": "mcporter@0.7.3", "integrity": "sha512-egoPVYqTnWb3NjRIxo+xc8OrAI0dlPrJm9pAiZx0pImuNIV5rKhGtTnIfH/Y1ldGPVu74ibj3KR5c9U/QSdQFA==", "tarballUrl": "https://registry.npmjs.org/mcporter/-/mcporter-0.7.3.tgz", - "directory": "agents/openclaw/mcporter-runtime" + "directory": "agents/openclaw/mcporter-runtime", + "lockSha256": "c31959d7950903f7477ca2e143b3f1f4adfd10f1961fe97db40cd72f62b84830" } ] } diff --git a/ci/reviewed-npm-lifecycle-allowlist.json b/ci/reviewed-npm-lifecycle-allowlist.json index 23db57f4a09..9d6bd33dbf4 100644 --- a/ci/reviewed-npm-lifecycle-allowlist.json +++ b/ci/reviewed-npm-lifecycle-allowlist.json @@ -25,7 +25,7 @@ "packageSpec": "openclaw@2026.7.1", "event": "postinstall", "manifestCommand": "node scripts/postinstall-bundled-plugins.mjs", - "explicitCommand": "node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs" + "explicitCommand": "node /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs" } ] } diff --git a/ci/source-shape-test-budget.json b/ci/source-shape-test-budget.json index fe88cdbc922..91d917907b0 100644 --- a/ci/source-shape-test-budget.json +++ b/ci/source-shape-test-budget.json @@ -391,6 +391,51 @@ "test": "cross-checks the allowlist against every production archive install boundary", "category": "security" }, + { + "file": "test/openclaw-locked-install.test.ts", + "test": "audits the same lock and rebuilds the base when its graph changes", + "category": "security" + }, + { + "file": "test/openclaw-locked-install.test.ts", + "test": "fails closed on missing required packages and symlinked package roots", + "category": "security" + }, + { + "file": "test/openclaw-locked-install.test.ts", + "test": "invokes the locked installer before exposing OpenClaw in %s", + "category": "compatibility" + }, + { + "file": "test/openclaw-locked-install.test.ts", + "test": "rejects $name even with a test-only matching lock digest", + "category": "security" + }, + { + "file": "test/openclaw-locked-install.test.ts", + "test": "rejects a same-registry tarball with a substituted package manifest", + "category": "security" + }, + { + "file": "test/openclaw-locked-install.test.ts", + "test": "rejects any lock byte tamper before registry metadata is consulted", + "category": "security" + }, + { + "file": "test/openclaw-locked-install.test.ts", + "test": "rejects symlinked package manifests", + "category": "security" + }, + { + "file": "test/reviewed-npm-audit-workflow.test.ts", + "test": "executes the trusted driver and helper against explicit target inputs", + "category": "security" + }, + { + "file": "test/reviewed-npm-audit-workflow.test.ts", + "test": "runs PR audits from trusted code and keeps the main audit on the checked-in action", + "category": "security" + }, { "file": "test/perl-critical-cve-remediation.test.ts", "test": "builds the fixed upstream release from a checksum-pinned source archive (#7338)", diff --git a/docs/security/openclaw-2026.7.1-dependency-review.md b/docs/security/openclaw-2026.7.1-dependency-review.md index f69f065d4f4..62b1c17ec8f 100644 --- a/docs/security/openclaw-2026.7.1-dependency-review.md +++ b/docs/security/openclaw-2026.7.1-dependency-review.md @@ -15,6 +15,15 @@ graph contains the newly disclosed critical `tar` advisory. The reviewed migration archives, so its manifest and lock move from `7.5.11` to `7.5.20`; the exact plugin graph reports no vulnerabilities after that update. +The production OpenClaw install uses the authoritative committed lock at +`agents/openclaw/openclaw-runtime/package-lock.json`, with SHA-256 +`82489f62febb12da52833c0b1f7f6969f7e21a098c565ef1f91342b1e5e32d88`. +NemoClaw derives that lock from the SRI-verified `openclaw@2026.7.1` archive +after replacing the reviewed affected `brace-expansion@5.0.7` and +`fast-uri@3.1.2` resolutions with `5.0.8` and `3.1.4`. +Image builds verify the lock digest and installed production graph before they +expose the OpenClaw binary. + The release lineage is unusually wide and divergent: the direct upstream comparison reports 4,407 commits ahead and 34 behind. The maintainer requested this exact stable release after reviewing that risk. The long-term source of @@ -52,15 +61,23 @@ whose amd64 config reports Node `22.23.1`. - `tar@7.5.20` (NemoClaw plugin direct dependency) - `sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==` - `https://registry.npmjs.org/tar/-/tar-7.5.20.tgz` +- `brace-expansion@5.0.8` (OpenClaw locked-runtime remediation) + - `sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==` + - `https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz` +- `fast-uri@3.1.4` (OpenClaw and mcporter locked-runtime remediation) + - `sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==` + - `https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz` ## Audit result and temporary dependency remediations -The exact reviewed archive graph contains `823` total dependencies and reports -`13` moderate, `0` high, and `0` critical vulnerabilities. The critical `tar` -finding that blocked the previous pin and the high Jaeger finding are gone. npm -audit expands the remaining `@hono/node-server` advisory through its affected -OpenClaw and MCP dependents, alongside the separate moderate `protobufjs` -finding. Both advisories are below the configured `high` threshold. +The reviewed audit materializes three production-compatible boundaries: the +remediated reviewed-archive graph, the committed OpenClaw runtime lock, and the +committed mcporter runtime lock. A real registry-backed run reports `0` high +and `0` critical vulnerabilities for all three boundaries, and npm registry +signature verification passes for both locked graphs. The critical `tar` +finding that blocked the previous pin and the high Jaeger, +`brace-expansion`, and `fast-uri` findings are gone. Lower-severity findings +remain visible below the configured `high` threshold. The independently installed `nemoclaw/` plugin graph reports `0` vulnerabilities after resolving its direct `tar` dependency to `7.5.20`. @@ -75,14 +92,30 @@ requirement remains inside the image's Node contract, and real ESM plus CommonJS Streamable HTTP transport construction/start/close probes cover the major-version compatibility boundary. -The SDK's locked AJV graph also requests `fast-uri@^3.0.1`; the newly published -`GHSA-v2hh-gcrm-f6hx` affects releases through `3.1.3`. The same manifest pins -the first compatible release outside that range, `3.1.4`. The resulting -`138`-dependency graph reports `0` known vulnerabilities; image assembly reports -lower-severity findings and blocks unaccepted high or critical findings through -the empty-by-default audit exception registry. Signature verification and the -exact committed lock remain mandatory. Remove either override when the declared -graph resolves to a reviewed patched release. +OpenClaw's `minimatch@10.2.5` edge originally resolved +`brace-expansion@5.0.7`, which is affected by `GHSA-mh99-v99m-4gvg`. +The reviewed OpenClaw remediation selects `5.0.8`, retains the declared +`balanced-match@^4.0.2` dependency shape, and fails if the upstream shrinkwrap +or replacement archive identity changes. + +The AJV graphs in OpenClaw and mcporter request `fast-uri@^3.0.1`. +The reviewed upstream OpenClaw shrinkwrap resolves `3.1.2`, and +`GHSA-v2hh-gcrm-f6hx` affects releases through `3.1.3`. +Both committed runtime locks select the first compatible release outside that +range, `3.1.4`. Image assembly reports lower-severity findings and blocks +unaccepted high or critical findings through the empty-by-default audit +exception registry. Signature verification and the exact committed locks +remain mandatory. + +The OpenClaw audit first applies the same fail-closed remediation to the +SRI-verified reviewed archive, then independently installs and verifies the +committed lock. The audit configuration pins the official npm registry origin, +package identity, tarball URL, SRI, and lock SHA-256. It rejects repository +path escapes, lock drift, registry-origin drift, or an installed graph that +does not match the lock before evaluating advisories. Remove the two OpenClaw +core replacements only after a supported OpenClaw archive publishes both +corrected transitive resolutions and the regenerated lock, installed-graph +verification, audit, and signature checks all pass. The published Slack and Microsoft Teams plugin archives bundle `axios@1.16.0`. That version is in the affected range for the newly disclosed Axios @@ -126,13 +159,15 @@ The child-process probe confirms these results: - valid `uberctx-test` baggage remains available; - a valid Jaeger header produces the expected trace and span context. -`scripts/lib/openclaw-npm-remediation.mts` verifies the original plugin and +`scripts/lib/openclaw-npm-remediation.mts` verifies each original package and replacement package identities before it writes the archive. It rejects an -upstream graph that no longer resolves Axios `1.16.0`. It then verifies the -deterministic remediated package-tree integrity before installation. This -canonical tree digest is independent of npm-generated tar metadata, which can -vary between npm patch releases without changing package contents. The -production plugin installer and `reviewed-npm-audit` use this same function. +upstream graph that no longer resolves the reviewed Axios, Jaeger, +`brace-expansion`, or `fast-uri` source versions and dependency shapes. It then +verifies the deterministic remediated package-tree integrity before +installation or lock generation. This canonical tree digest is independent of +npm-generated tar metadata, which can vary between npm patch releases without +changing package contents. The production plugin installer, OpenClaw lock +workflow, and `reviewed-npm-audit` use this same function. The tree hash opens each regular file without following symbolic links and validates the opened descriptor before it reads the content. This keeps the metadata and content checks bound to the same file. @@ -146,10 +181,12 @@ Issue #7337 tracks removal of the Jaeger branch and its exact replacement pins. The reviewed installer verifies each registry identity and downloaded tarball integrity. `scripts/lib/reviewed-npm-archive.mts` uses `npm pack --json` and -rejects reported archive filenames containing unsafe archive paths. Its checks -bind reviewed npm installs to verified local archives and check each reviewed -npm plugin registry integrity. The helper returns only the verified local `.tgz` -path. +rejects reported archive filenames containing unsafe archive paths. Its archive +checks bind reviewed npm installs to verified local archives: they compare each +reviewed npm plugin registry integrity, and the helper returns only the verified +local `.tgz` path. Its locked-runtime +checks bind the OpenClaw and mcporter installs to exact committed lock digests, +the official registry origin, and post-install graph verification. ## OpenClaw Compiled-Dist Patch Runtime Boundary diff --git a/scripts/audit-reviewed-npm-graph.mts b/scripts/audit-reviewed-npm-graph.mts index 3d4bd80e329..e2e46dbc044 100755 --- a/scripts/audit-reviewed-npm-graph.mts +++ b/scripts/audit-reviewed-npm-graph.mts @@ -8,7 +8,11 @@ import os from "node:os"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { remediateReviewedOpenClawPluginArchive } from "./lib/openclaw-npm-remediation.mts"; -import { packReviewedNpmArchive, verifyReviewedNpmMetadata } from "./lib/reviewed-npm-archive.mts"; +import { + packReviewedNpmArchive, + verifyInstalledNpmLock, + verifyReviewedNpmLock, +} from "./lib/reviewed-npm-archive.mts"; import { assertExceptionGraphs, readAuditExceptionRegistry, @@ -22,7 +26,8 @@ type ReviewedPackage = Readonly<{ packageSpec: string; tarballUrl: string; }>; -type LockedGraph = ReviewedPackage & Readonly<{ directory: string; id: string }>; +type LockedGraph = ReviewedPackage & + Readonly<{ directory: string; id: string; lockSha256: string }>; type AuditConfig = Readonly<{ archivePackages: readonly ReviewedPackage[]; archiveGraphId: string; @@ -30,26 +35,71 @@ type AuditConfig = Readonly<{ exceptionFile: string; lockedGraphs: readonly LockedGraph[]; nodeVersion: string; + registryOrigin: string; schemaVersion: 2; severityThreshold: Severity; }>; -const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); -const CONFIG_PATH = path.join(REPO_ROOT, "ci", "reviewed-npm-audit.json"); +const TRUSTED_REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const TARGET_REPO_ROOT = fs.realpathSync( + path.resolve(process.env.NEMOCLAW_REVIEWED_NPM_AUDIT_TARGET_ROOT ?? TRUSTED_REPO_ROOT), +); +const CONFIG_PATH = resolveTrustedAuditConfigPath(TRUSTED_REPO_ROOT); const SEVERITIES: readonly Severity[] = ["info", "low", "moderate", "high", "critical"]; +const OPENCLAW_DOMEXCEPTION_ALIAS = { + actualName: "@nolyfill/domexception", + aliasPackagePath: "node_modules/openclaw/node_modules/node-domexception", + actualPackagePath: "node_modules/openclaw/node_modules/@nolyfill/domexception", + integrity: + "sha512-tlc/FcYIv5i8RYsl2iDil4A0gOihaas1R5jPcIC4Zw3GhjKsVilw90aHcVlhZPTBLGBzd379S+VcnsDjd9ChiA==", + requesterPackagePath: "node_modules/openclaw/node_modules/fetch-blob", + requestedRange: "^1.0.0", + resolved: "https://registry.npmjs.org/@nolyfill/domexception/-/domexception-1.0.28.tgz", + version: "1.0.28", +} as const; -function repositoryPath(relativePath: string, label: string): string { - const resolved = path.resolve(REPO_ROOT, relativePath); - if ( - !relativePath || - path.isAbsolute(relativePath) || - !resolved.startsWith(`${REPO_ROOT}${path.sep}`) - ) { - throw new Error(`${label} must stay inside the repository`); +export function resolvePathWithinRoot(root: string, relativePath: string, label: string): string { + if (!relativePath || path.isAbsolute(relativePath)) { + throw new Error(`${label} must be a nonempty relative path`); + } + const canonicalRoot = fs.realpathSync(path.resolve(root)); + const resolved = path.resolve(canonicalRoot, relativePath); + if (!resolved.startsWith(`${canonicalRoot}${path.sep}`)) { + throw new Error(`${label} escapes its repository root: ${relativePath}`); + } + let current = canonicalRoot; + for (const component of path.relative(canonicalRoot, resolved).split(path.sep)) { + current = path.join(current, component); + let stat: fs.Stats; + try { + stat = fs.lstatSync(current); + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") break; + throw error; + } + if (stat.isSymbolicLink()) { + throw new Error(`${label} contains a symbolic-link component: ${relativePath}`); + } } return resolved; } +export function resolveTrustedAuditConfigPath(trustedRoot: string): string { + return resolvePathWithinRoot( + trustedRoot, + "ci/reviewed-npm-audit.json", + "trusted reviewed npm audit configuration", + ); +} + +function trustedRepositoryPath(relativePath: string, label: string): string { + return resolvePathWithinRoot(TRUSTED_REPO_ROOT, relativePath, label); +} + +function targetRepositoryPath(relativePath: string, label: string): string { + return resolvePathWithinRoot(TARGET_REPO_ROOT, relativePath, label); +} + function run(command: string, args: readonly string[], cwd: string) { const result = spawnSync(command, args, { cwd, @@ -74,10 +124,18 @@ function readConfig(): AuditConfig { !parsed.archiveGraphId || typeof parsed.exceptionFile !== "string" || !parsed.exceptionFile || + typeof parsed.registryOrigin !== "string" || + !parsed.registryOrigin || !Array.isArray(parsed.archivePackages) || !Array.isArray(parsed.lockedGraphs) || parsed.lockedGraphs.some( - (graph) => typeof graph.id !== "string" || !graph.id || typeof graph.directory !== "string", + (graph) => + typeof graph.id !== "string" || + !graph.id || + typeof graph.directory !== "string" || + !graph.directory || + typeof graph.lockSha256 !== "string" || + !/^[0-9a-f]{64}$/.test(graph.lockSha256), ) ) { throw new Error("ci/reviewed-npm-audit.json is invalid"); @@ -121,31 +179,170 @@ function materializeArchiveGraph(packages: readonly ReviewedPackage[], tempRoot: return graphDirectory; } -function materializeLockedGraph(graph: LockedGraph, tempRoot: string): string { - verifyReviewedNpmMetadata({ +function materializeLockedGraph( + graph: LockedGraph, + tempRoot: string, + registryOrigin: string, +): string { + const sourcePackage = targetRepositoryPath( + path.join(graph.directory, "package.json"), + `${graph.label} package manifest`, + ); + const sourceLock = targetRepositoryPath( + path.join(graph.directory, "package-lock.json"), + `${graph.label} lockfile`, + ); + verifyReviewedNpmLock({ expectedIntegrity: graph.integrity, + expectedLockSha256: graph.lockSha256, label: graph.label, + lockfilePath: sourceLock, packageSpec: graph.packageSpec, + registryOrigin, tarballUrl: graph.tarballUrl, }); - const source = repositoryPath(graph.directory, `${graph.label} directory`); const destination = path.join(tempRoot, `locked-${path.basename(graph.directory)}`); fs.mkdirSync(destination); - for (const filename of ["package.json", "package-lock.json"]) { - fs.copyFileSync(path.join(source, filename), path.join(destination, filename)); - } + fs.copyFileSync(sourcePackage, path.join(destination, "package.json")); + fs.copyFileSync(sourceLock, path.join(destination, "package-lock.json")); run("npm", ["ci", "--ignore-scripts", "--omit=dev", "--no-audit", "--no-fund"], destination); + verifyInstalledNpmLock({ + expectedLockSha256: graph.lockSha256, + installRoot: destination, + label: graph.label, + lockfilePath: path.join(destination, "package-lock.json"), + }); return destination; } +function readJsonObject(file: string, label: string): Record { + const parsed = JSON.parse(fs.readFileSync(file, "utf-8")) as unknown; + if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { + throw new Error(`${label} must be a JSON object`); + } + return parsed as Record; +} + +function assertRegularFile(file: string, label: string): void { + const stat = fs.lstatSync(file); + if (!stat.isFile() || stat.isSymbolicLink()) { + throw new Error(`${label} must be a regular file`); + } +} + +export function normalizeOpenClawSignatureAlias(directory: string): void { + const { + actualName, + actualPackagePath, + aliasPackagePath, + integrity, + requesterPackagePath, + requestedRange, + resolved, + version, + } = OPENCLAW_DOMEXCEPTION_ALIAS; + const lockfile = path.join(directory, "package-lock.json"); + const aliasDirectory = path.join(directory, aliasPackagePath); + const actualDirectory = path.join(directory, actualPackagePath); + const aliasManifestFile = path.join(aliasDirectory, "package.json"); + const requesterManifestFile = path.join(directory, requesterPackagePath, "package.json"); + for (const [file, label] of [ + [lockfile, "OpenClaw signature-audit lock"], + [aliasManifestFile, "OpenClaw aliased package manifest"], + [requesterManifestFile, "OpenClaw alias requester manifest"], + ] as const) { + assertRegularFile(file, label); + } + if (fs.existsSync(actualDirectory)) { + throw new Error(`OpenClaw signature-audit destination already exists: ${actualPackagePath}`); + } + + const lock = readJsonObject(lockfile, "OpenClaw signature-audit lock"); + const packages = lock.packages as Record | undefined; + const aliasEntry = packages?.[aliasPackagePath]; + const requesterEntry = packages?.[requesterPackagePath]; + if ( + !packages || + !aliasEntry || + aliasEntry.name !== actualName || + aliasEntry.version !== version || + aliasEntry.resolved !== resolved || + aliasEntry.integrity !== integrity || + packages[actualPackagePath] || + requesterEntry?.dependencies?.["node-domexception"] !== requestedRange || + requesterEntry.dependencies[actualName] !== undefined + ) { + throw new Error("OpenClaw signature-audit alias lock identity drifted"); + } + const aliasManifest = readJsonObject(aliasManifestFile, "OpenClaw aliased package manifest"); + const requesterManifest = readJsonObject( + requesterManifestFile, + "OpenClaw alias requester manifest", + ); + if ( + aliasManifest.name !== actualName || + aliasManifest.version !== version || + requesterManifest.dependencies?.["node-domexception"] !== requestedRange || + requesterManifest.dependencies?.[actualName] !== undefined + ) { + throw new Error("OpenClaw signature-audit installed alias identity drifted"); + } + + packages[actualPackagePath] = aliasEntry; + delete packages[aliasPackagePath]; + delete requesterEntry.dependencies["node-domexception"]; + requesterEntry.dependencies[actualName] = version; + delete requesterManifest.dependencies["node-domexception"]; + requesterManifest.dependencies[actualName] = version; + fs.mkdirSync(path.dirname(actualDirectory), { recursive: true }); + fs.renameSync(aliasDirectory, actualDirectory); + fs.writeFileSync(lockfile, `${JSON.stringify(lock, null, 2)}\n`); + fs.writeFileSync(requesterManifestFile, `${JSON.stringify(requesterManifest, null, 2)}\n`); +} + +function auditLockedGraph( + graph: LockedGraph, + index: number, + config: AuditConfig, + tempRoot: string, + exceptionFile: string, + artifactDirectory: string, + npmVersion: string, +) { + const directory = materializeLockedGraph(graph, tempRoot, config.registryOrigin); + const result = runReviewedNpmAudit({ + directory, + exceptionFile, + graph: graph.id, + provenance: { + label: graph.label, + nodeVersion: process.version, + npmVersion, + packageSpecs: [graph.packageSpec], + }, + reportFile: path.join(artifactDirectory, `locked-graph-${index + 1}.json`), + resultFile: path.join(artifactDirectory, `locked-graph-${index + 1}-policy.json`), + threshold: config.severityThreshold, + throwOnBlock: false, + }); + if (graph.id === "openclaw-runtime") { + normalizeOpenClawSignatureAlias(directory); + } + run("npm", ["audit", "signatures", "--omit=dev"], directory); + return result; +} + function main(): void { const config = readConfig(); const expectedNode = `v${config.nodeVersion}`; if (process.version !== expectedNode) { throw new Error(`reviewed npm audit requires Node ${expectedNode}; running ${process.version}`); } - const artifactDirectory = repositoryPath(config.artifactDirectory, "audit artifact directory"); - const exceptionFile = repositoryPath(config.exceptionFile, "npm audit exception file"); + const artifactDirectory = targetRepositoryPath( + process.env.NEMOCLAW_REVIEWED_NPM_AUDIT_REPORT_DIR ?? config.artifactDirectory, + "audit artifact directory", + ); + const exceptionFile = trustedRepositoryPath(config.exceptionFile, "npm audit exception file"); const exceptionRegistry = readAuditExceptionRegistry(exceptionFile); assertExceptionGraphs( exceptionRegistry.policy, @@ -153,7 +350,7 @@ function main(): void { ); fs.rmSync(artifactDirectory, { recursive: true, force: true }); fs.mkdirSync(artifactDirectory, { recursive: true }); - const npmVersion = run("npm", ["--version"], REPO_ROOT).stdout.trim(); + const npmVersion = run("npm", ["--version"], TRUSTED_REPO_ROOT).stdout.trim(); const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-reviewed-npm-audit-")); try { const reports = [ @@ -177,21 +374,15 @@ function main(): void { }, ...config.lockedGraphs.map((graph, index) => ({ label: graph.label, - result: runReviewedNpmAudit({ - directory: materializeLockedGraph(graph, tempRoot), + result: auditLockedGraph( + graph, + index, + config, + tempRoot, exceptionFile, - graph: graph.id, - provenance: { - label: graph.label, - nodeVersion: process.version, - npmVersion, - packageSpecs: [graph.packageSpec], - }, - reportFile: path.join(artifactDirectory, `locked-graph-${index + 1}.json`), - resultFile: path.join(artifactDirectory, `locked-graph-${index + 1}-policy.json`), - threshold: config.severityThreshold, - throwOnBlock: false, - }), + artifactDirectory, + npmVersion, + ), })), ]; const failures: string[] = []; diff --git a/scripts/lib/openclaw-npm-remediation.mts b/scripts/lib/openclaw-npm-remediation.mts index 50675333b3e..4561999fb1c 100755 --- a/scripts/lib/openclaw-npm-remediation.mts +++ b/scripts/lib/openclaw-npm-remediation.mts @@ -27,7 +27,7 @@ type JsonObject = Record; type Remediation = Readonly<{ expectedPatchedMetadataIntegrity?: string; expectedPatchedTreeIntegrity?: string; - kind: "axios" | "core" | "jaeger" | "legacy-core"; + kind: "axios" | "core" | "current-core" | "jaeger" | "legacy-core"; version: "2026.3.11" | "2026.6.10" | "2026.7.1"; }>; @@ -85,6 +85,15 @@ const BRACE_EXPANSION_INTEGRITY = "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA=="; const BRACE_EXPANSION_TARBALL = "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz"; +const CURRENT_BRACE_EXPANSION_VERSION = "5.0.8"; +const CURRENT_BRACE_EXPANSION_INTEGRITY = + "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg=="; +const CURRENT_BRACE_EXPANSION_TARBALL = + "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz"; +const CURRENT_FAST_URI_VERSION = "3.1.4"; +const CURRENT_FAST_URI_INTEGRITY = + "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw=="; +const CURRENT_FAST_URI_TARBALL = "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz"; const JAEGER_PROPAGATOR_VERSION = "2.9.0"; const JAEGER_PROPAGATOR_INTEGRITY = "sha512-4mYGty27rYvSM0jtp1ZUOqd3LfVRCYg9H5G9OFzSx5HViYToU21MFhWfco7x1HwXr7ER8yGOiCIHZUwjPksc0Q=="; @@ -139,6 +148,14 @@ const REMEDIATIONS: Readonly> = Object.freeze({ kind: "core", version: "2026.6.10", }, + // openclaw/openclaw#113584: remove after a supported OpenClaw archive + // publishes both corrected transitive resolutions in its shrinkwrap. + "openclaw@2026.7.1": { + expectedPatchedTreeIntegrity: + "sha512-/D/vAtyFEvd0TvYxFBfBN/chwW8WGaAy8bH40ojjJSRuN+P461qnbfiMTHydA7asXN/69y2NnQHs7b3pDh8+0g==", + kind: "current-core", + version: "2026.7.1", + }, "openclaw@2026.3.11": { kind: "legacy-core", expectedPatchedMetadataIntegrity: @@ -497,6 +514,62 @@ export function patchOpenClawCorePackageGraph(packageDirectory: string): void { writeJson(shrinkwrapPath, shrinkwrap); } +export function patchCurrentOpenClawCorePackageGraph(packageDirectory: string): void { + const packageJsonPath = join(packageDirectory, "package.json"); + const shrinkwrapPath = join(packageDirectory, "npm-shrinkwrap.json"); + const packageJson = readJson(packageJsonPath); + requirePackageIdentity(packageJson, "openclaw", "2026.7.1", "OpenClaw core"); + if ( + packageJson.dependencies?.minimatch !== "10.2.5" || + packageJson.dependencies?.["@modelcontextprotocol/sdk"] !== "1.29.0" || + packageJson.dependencies?.["brace-expansion"] !== undefined || + packageJson.dependencies?.["fast-uri"] !== undefined + ) { + throw new Error("openclaw@2026.7.1 dependency boundary changed after review"); + } + + const shrinkwrap = readJson(shrinkwrapPath); + if (shrinkwrap.lockfileVersion !== 3 || !shrinkwrap.packages?.[""]) { + throw new Error("openclaw@2026.7.1 must ship an npm lockfileVersion 3 shrinkwrap"); + } + const packages = shrinkwrap.packages as JsonObject; + const root = packages[""] as JsonObject; + requirePackageIdentity(root, "openclaw", "2026.7.1", "OpenClaw shrinkwrap root"); + const braceExpansion = packages["node_modules/brace-expansion"] as JsonObject | undefined; + const fastUri = packages["node_modules/fast-uri"] as JsonObject | undefined; + const minimatch = packages["node_modules/minimatch"] as JsonObject | undefined; + const ajv = packages["node_modules/ajv"] as JsonObject | undefined; + if ( + braceExpansion?.version !== "5.0.7" || + braceExpansion.resolved !== + "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz" || + braceExpansion.integrity !== + "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==" || + braceExpansion.dependencies?.["balanced-match"] !== "^4.0.2" || + minimatch?.dependencies?.["brace-expansion"] !== "^5.0.5" + ) { + throw new Error("openclaw@2026.7.1 brace-expansion layout changed after review"); + } + if ( + fastUri?.version !== "3.1.2" || + fastUri.resolved !== "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz" || + fastUri.integrity !== + "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==" || + ajv?.version !== "8.20.0" || + ajv.dependencies?.["fast-uri"] !== "^3.0.1" + ) { + throw new Error("openclaw@2026.7.1 fast-uri layout changed after review"); + } + + braceExpansion.version = CURRENT_BRACE_EXPANSION_VERSION; + braceExpansion.resolved = CURRENT_BRACE_EXPANSION_TARBALL; + braceExpansion.integrity = CURRENT_BRACE_EXPANSION_INTEGRITY; + fastUri.version = CURRENT_FAST_URI_VERSION; + fastUri.resolved = CURRENT_FAST_URI_TARBALL; + fastUri.integrity = CURRENT_FAST_URI_INTEGRITY; + writeJson(shrinkwrapPath, shrinkwrap); +} + export function patchLegacyOpenClawCorePackageGraph(packageDirectory: string): void { const packageJsonPath = join(packageDirectory, "package.json"); const bundledTarPackageJsonPath = join(packageDirectory, "node_modules", "tar", "package.json"); @@ -768,6 +841,61 @@ export function buildRemediatedOpenClawPluginArchive( join(sourcePackage, "node_modules", "@openclaw", "fs-safe"), ); patchOpenClawCorePackageGraph(sourcePackage); + } else if (remediation.kind === "current-core") { + const braceExpansionArchive = packReplacement( + `brace-expansion@${CURRENT_BRACE_EXPANSION_VERSION}`, + CURRENT_BRACE_EXPANSION_INTEGRITY, + CURRENT_BRACE_EXPANSION_TARBALL, + remediationRoot, + env, + ); + const fastUriArchive = packReplacement( + `fast-uri@${CURRENT_FAST_URI_VERSION}`, + CURRENT_FAST_URI_INTEGRITY, + CURRENT_FAST_URI_TARBALL, + remediationRoot, + env, + ); + const braceExpansionPackage = extractArchive( + braceExpansionArchive.archivePath, + join(remediationRoot, "brace-expansion"), + remediationRoot, + env, + ); + const fastUriPackage = extractArchive( + fastUriArchive.archivePath, + join(remediationRoot, "fast-uri"), + remediationRoot, + env, + ); + const braceExpansionPackageJson = readJson(join(braceExpansionPackage, "package.json")); + const fastUriPackageJson = readJson(join(fastUriPackage, "package.json")); + requirePackageIdentity( + braceExpansionPackageJson, + "brace-expansion", + CURRENT_BRACE_EXPANSION_VERSION, + "OpenClaw brace-expansion remediation package", + ); + requireDependencyShape( + braceExpansionPackageJson, + { "balanced-match": "^4.0.2" }, + `brace-expansion@${CURRENT_BRACE_EXPANSION_VERSION}`, + ); + requirePackageIdentity( + fastUriPackageJson, + "fast-uri", + CURRENT_FAST_URI_VERSION, + "OpenClaw fast-uri remediation package", + ); + if ( + fastUriPackageJson.dependencies !== undefined && + Object.keys(fastUriPackageJson.dependencies).length !== 0 + ) { + throw new Error( + `fast-uri@${CURRENT_FAST_URI_VERSION} dependency graph changed; review the remediation before updating it`, + ); + } + patchCurrentOpenClawCorePackageGraph(sourcePackage); } else if (remediation.kind === "legacy-core") { const bundledTarPath = join(sourcePackage, "node_modules", "tar"); if (existsSync(bundledTarPath)) { diff --git a/scripts/lib/reviewed-npm-archive.mts b/scripts/lib/reviewed-npm-archive.mts index 88a18687ff1..6cdb8291629 100755 --- a/scripts/lib/reviewed-npm-archive.mts +++ b/scripts/lib/reviewed-npm-archive.mts @@ -3,7 +3,17 @@ // SPDX-License-Identifier: Apache-2.0 import { spawnSync } from "node:child_process"; -import { existsSync, lstatSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { createHash } from "node:crypto"; +import { + closeSync, + existsSync, + fstatSync, + lstatSync, + mkdtempSync, + openSync, + readFileSync, + rmSync, +} from "node:fs"; import { tmpdir } from "node:os"; import { isAbsolute, join, resolve, sep } from "node:path"; import { pathToFileURL } from "node:url"; @@ -31,6 +41,20 @@ export type ReviewedNpmCacheRequest = Readonly<{ tempDirectory?: string; }>; +export type ReviewedNpmLockRequest = ReviewedNpmArchiveRequest & + Readonly<{ + expectedLockSha256: string; + lockfilePath: string; + registryOrigin: string; + }>; + +export type ReviewedInstalledNpmLockRequest = Readonly<{ + expectedLockSha256: string; + installRoot: string; + label: string; + lockfilePath: string; +}>; + export type ReviewedNpmMetadata = Readonly<{ integrity: string; tarballUrl: string; @@ -196,10 +220,7 @@ function normalizeRegistryOrigin(value: string): string { return parsed.origin; } -function readReviewedLockPackages( - lockfilePath: string, - registryOrigin: string, -): readonly ReviewedNpmArchiveRequest[] { +function readReviewedLock(lockfilePath: string): Record> { let lock: unknown; try { lock = JSON.parse(readFileSync(lockfilePath, "utf-8")); @@ -211,32 +232,66 @@ function readReviewedLockPackages( } const lockRecord = lock as Record; if (lockRecord.lockfileVersion !== 3) { - throw new Error("reviewed npm cache requires lockfileVersion 3"); + throw new Error("reviewed npm lock requires lockfileVersion 3"); } const packages = lockRecord.packages; if (typeof packages !== "object" || packages === null || Array.isArray(packages)) { throw new Error("reviewed npm lockfile is missing its packages map"); } + return packages as Record>; +} + +function parseExactPackageSpec(packageSpec: string): { name: string; version: string } { + if (!EXACT_NPM_PACKAGE_SPEC.test(packageSpec)) { + throw new Error(`reviewed npm lock must use an exact npm package spec: ${packageSpec}`); + } + const separator = packageSpec.lastIndexOf("@"); + return { name: packageSpec.slice(0, separator), version: packageSpec.slice(separator + 1) }; +} + +function packageNameFromLockLocation(location: string): string { + const marker = "node_modules/"; + const nestedMarkerIndex = location.lastIndexOf(`/${marker}`); + const markerIndex = + nestedMarkerIndex >= 0 ? nestedMarkerIndex + 1 : location.startsWith(marker) ? 0 : -1; + const packageName = markerIndex >= 0 ? location.slice(markerIndex + marker.length) : ""; + if (!packageName) { + throw new Error(`reviewed npm lock has an unsupported package location: ${location}`); + } + return packageName; +} +function verifyReviewedLockDigest( + lockfilePath: string, + expectedLockSha256: string, + label: string, +): void { + if (!/^[0-9a-f]{64}$/.test(expectedLockSha256)) { + throw new Error(`${label} must use a committed lowercase SHA-256 lock identity`); + } + const actualLockSha256 = createHash("sha256").update(readFileSync(lockfilePath)).digest("hex"); + if (actualLockSha256 !== expectedLockSha256) { + throw new Error( + `${label} lock SHA-256 mismatch\nExpected: ${expectedLockSha256}\nActual: ${actualLockSha256}`, + ); + } +} + +function readReviewedLockPackages( + packages: Readonly>>, + lockfilePath: string, + registryOrigin: string, +): readonly ReviewedNpmArchiveRequest[] { const reviewed: ReviewedNpmArchiveRequest[] = []; - const identities = new Set(); + const identities = new Map(); for (const [location, value] of Object.entries(packages)) { if (location === "") continue; - const marker = "node_modules/"; - const nestedMarkerIndex = location.lastIndexOf(`/${marker}`); - const markerIndex = location.startsWith(marker) - ? 0 - : nestedMarkerIndex >= 0 - ? nestedMarkerIndex + 1 - : -1; - const packageName = markerIndex >= 0 ? location.slice(markerIndex + marker.length) : ""; - if (!packageName) { - throw new Error(`reviewed npm lock has an unsupported package location: ${location}`); - } if (typeof value !== "object" || value === null || Array.isArray(value)) { throw new Error(`reviewed npm lock has an invalid package record: ${location}`); } const record = value as Record; + const locationName = packageNameFromLockLocation(location); + const packageName = typeof record.name === "string" ? record.name : locationName; const version = typeof record.version === "string" ? record.version : ""; const packageSpec = `${packageName}@${version}`; const expectedIntegrity = typeof record.integrity === "string" ? record.integrity : ""; @@ -260,21 +315,160 @@ function readReviewedLockPackages( ) { throw new Error(`reviewed npm lock package must use the reviewed registry: ${location}`); } - if (identities.has(packageSpec)) { - throw new Error(`reviewed npm lock repeats package identity: ${packageSpec}`); + const prior = identities.get(packageSpec); + if (prior) { + if (prior.expectedIntegrity !== expectedIntegrity || prior.tarballUrl !== tarballUrl) { + throw new Error(`reviewed npm lock has conflicting package identity: ${packageSpec}`); + } + continue; } - identities.add(packageSpec); - reviewed.push({ + const request = { expectedIntegrity, label: `locked npm package ${packageSpec}`, packageSpec, tarballUrl, - }); + }; + identities.set(packageSpec, request); + reviewed.push(request); + } + if (reviewed.length === 0) { + throw new Error(`reviewed npm lock contains no packages: ${lockfilePath}`); } - if (reviewed.length === 0) throw new Error("reviewed npm lock contains no packages"); return reviewed; } +export function verifyReviewedNpmLock( + request: ReviewedNpmLockRequest, + npmRunner: NpmRunner = runNpm, +): readonly string[] { + requireReviewedRequest(request); + verifyReviewedLockDigest(request.lockfilePath, request.expectedLockSha256, request.label); + const registryOrigin = normalizeRegistryOrigin(request.registryOrigin); + const packages = readReviewedLock(request.lockfilePath); + const { name, version } = parseExactPackageSpec(request.packageSpec); + const root = packages[""]; + if (!root || typeof root !== "object" || Array.isArray(root)) { + throw new Error("reviewed npm lock is missing its root package record"); + } + const rootDependencies = root.dependencies; + const rootOptionalDependencies = root.optionalDependencies; + if ( + typeof rootDependencies !== "object" || + rootDependencies === null || + Array.isArray(rootDependencies) || + Object.keys(rootDependencies).length !== 1 || + (rootDependencies as Record)[name] !== version || + (rootOptionalDependencies !== undefined && + (typeof rootOptionalDependencies !== "object" || + rootOptionalDependencies === null || + Array.isArray(rootOptionalDependencies) || + Object.keys(rootOptionalDependencies).length > 0)) + ) { + throw new Error(`reviewed npm lock root must depend only on ${request.packageSpec}`); + } + + const topLevel = packages[`node_modules/${name}`]; + if (!topLevel || typeof topLevel !== "object" || Array.isArray(topLevel)) { + throw new Error(`reviewed npm lock is missing ${request.packageSpec}`); + } + if (topLevel.version !== version) { + throw new Error( + `reviewed npm lock version mismatch for ${name}: expected ${version}, found ${String(topLevel.version ?? "missing")}`, + ); + } + if (topLevel.integrity !== request.expectedIntegrity) { + throw new Error(`reviewed npm lock integrity mismatch for ${request.packageSpec}`); + } + if (topLevel.resolved !== request.tarballUrl) { + throw new Error(`reviewed npm lock tarball URL mismatch for ${request.packageSpec}`); + } + if (Object.prototype.hasOwnProperty.call(topLevel, "hasShrinkwrap")) { + throw new Error( + `reviewed npm lock must be authoritative for ${request.packageSpec}; nested shrinkwrap delegation is not allowed`, + ); + } + + const reviewed = readReviewedLockPackages(packages, request.lockfilePath, registryOrigin); + verifyReviewedNpmMetadata(request, npmRunner); + return reviewed.map(({ packageSpec }) => packageSpec); +} + +export function verifyInstalledNpmLock( + request: ReviewedInstalledNpmLockRequest, +): readonly string[] { + verifyReviewedLockDigest(request.lockfilePath, request.expectedLockSha256, request.label); + const packages = readReviewedLock(request.lockfilePath); + const installRoot = resolve(request.installRoot); + const nodeModulesRoot = resolve(installRoot, "node_modules"); + const verified: string[] = []; + + for (const [location, record] of Object.entries(packages)) { + if (location === "") continue; + const locationName = packageNameFromLockLocation(location); + const expectedName = typeof record.name === "string" ? record.name : locationName; + const expectedVersion = typeof record.version === "string" ? record.version : ""; + const packageSpec = `${expectedName}@${expectedVersion}`; + if (!EXACT_NPM_PACKAGE_SPEC.test(packageSpec)) { + throw new Error(`${request.label} has an invalid locked package identity: ${packageSpec}`); + } + const packageDirectory = resolve(installRoot, location); + if (!packageDirectory.startsWith(`${nodeModulesRoot}${sep}`)) { + throw new Error(`${request.label} has an unsafe installed package path: ${location}`); + } + let packageEntry; + try { + packageEntry = lstatSync(packageDirectory); + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + if (record.optional === true) continue; + throw new Error(`${request.label} is missing installed package: ${packageSpec}`); + } + throw error; + } + if (!packageEntry.isDirectory() || packageEntry.isSymbolicLink()) { + throw new Error( + `${request.label} installed package must be a non-symlink directory: ${location}`, + ); + } + + const manifestPath = join(packageDirectory, "package.json"); + let manifest: unknown; + let manifestDescriptor: number | undefined; + try { + manifestDescriptor = openSync(manifestPath, "r"); + const openedManifestEntry = fstatSync(manifestDescriptor); + const pathManifestEntry = lstatSync(manifestPath); + if ( + !openedManifestEntry.isFile() || + !pathManifestEntry.isFile() || + pathManifestEntry.isSymbolicLink() || + openedManifestEntry.dev !== pathManifestEntry.dev || + openedManifestEntry.ino !== pathManifestEntry.ino + ) { + throw new Error("manifest must be a non-symlink regular file"); + } + manifest = JSON.parse(readFileSync(manifestDescriptor, "utf-8")); + } catch (error) { + throw new Error( + `${request.label} installed package manifest is unreadable: ${location}: ${String(error)}`, + ); + } finally { + if (manifestDescriptor !== undefined) closeSync(manifestDescriptor); + } + if (typeof manifest !== "object" || manifest === null || Array.isArray(manifest)) { + throw new Error(`${request.label} installed package manifest is invalid: ${location}`); + } + const installed = manifest as Record; + if (installed.name !== expectedName || installed.version !== expectedVersion) { + throw new Error( + `${request.label} installed package identity mismatch at ${location}: expected ${packageSpec}, found ${String(installed.name ?? "missing")}@${String(installed.version ?? "missing")}`, + ); + } + verified.push(packageSpec); + } + return verified; +} + export function verifyReviewedNpmCache( request: ReviewedNpmCacheRequest, npmRunner: NpmRunner = runNpm, @@ -292,7 +486,11 @@ export function verifyReviewedNpmCache( } const registryOrigin = normalizeRegistryOrigin(request.registryOrigin); - const packages = readReviewedLockPackages(request.lockfilePath, registryOrigin); + const packages = readReviewedLockPackages( + readReviewedLock(request.lockfilePath), + request.lockfilePath, + registryOrigin, + ); const env = { ...process.env, ...request.env, @@ -325,17 +523,30 @@ export function verifyReviewedNpmCache( type ArchiveCliOptions = ReviewedNpmArchiveRequest & Readonly<{ mode: "archive"; verifyOnly: boolean }>; type CacheCliOptions = ReviewedNpmCacheRequest & Readonly<{ mode: "cache" }>; -type CliOptions = ArchiveCliOptions | CacheCliOptions; +type LockCliOptions = ReviewedNpmLockRequest & Readonly<{ mode: "lock" }>; +type InstalledLockCliOptions = ReviewedInstalledNpmLockRequest & + Readonly<{ mode: "installed-lock" }>; +type CliOptions = ArchiveCliOptions | CacheCliOptions | LockCliOptions | InstalledLockCliOptions; function parseCliOptions(argv: readonly string[]): CliOptions { const values = new Map(); let verifyOnly = false; + let verifyLock = false; + let verifyInstalledLock = false; for (let index = 0; index < argv.length; index += 1) { const arg = argv[index]; if (arg === "--verify-only") { verifyOnly = true; continue; } + if (arg === "--verify-lock") { + verifyLock = true; + continue; + } + if (arg === "--verify-installed-lock") { + verifyInstalledLock = true; + continue; + } if (!arg?.startsWith("--")) throw new Error(`Unknown argument: ${arg ?? ""}`); const value = argv[index + 1]; if (!value || value.startsWith("--")) throw new Error(`${arg} requires a value`); @@ -347,9 +558,11 @@ function parseCliOptions(argv: readonly string[]): CliOptions { if (!value) throw new Error(`${name} is required`); return value; }; - if (values.has("--lockfile") || values.has("--cache") || values.has("--registry-origin")) { + if (values.has("--cache")) { if ( verifyOnly || + verifyLock || + verifyInstalledLock || values.has("--package-spec") || values.has("--integrity") || values.has("--tarball-url") || @@ -366,6 +579,40 @@ function parseCliOptions(argv: readonly string[]): CliOptions { tempDirectory: values.get("--temp-directory"), }; } + if (verifyInstalledLock) { + if (verifyOnly || verifyLock || values.has("--cache") || values.has("--registry-origin")) { + throw new Error("installed npm lock verification cannot be combined with other modes"); + } + return { + expectedLockSha256: required("--lock-sha256"), + installRoot: required("--install-root"), + label: required("--label"), + lockfilePath: required("--lockfile"), + mode: "installed-lock", + }; + } + if (verifyLock) { + if (verifyOnly || values.has("--cache")) { + throw new Error("reviewed npm lock verification cannot be combined with other modes"); + } + return { + expectedIntegrity: required("--integrity"), + expectedLockSha256: required("--lock-sha256"), + label: required("--label"), + lockfilePath: required("--lockfile"), + mode: "lock", + npmExecutable: process.env.NEMOCLAW_REVIEWED_NPM_EXECUTABLE, + packageSpec: required("--package-spec"), + registryOrigin: required("--registry-origin"), + tarballUrl: required("--tarball-url"), + tempDirectory: values.get("--temp-directory"), + }; + } + if (values.has("--lockfile") || values.has("--registry-origin") || values.has("--install-root")) { + throw new Error( + "--lockfile, --registry-origin, and --install-root require a matching lock mode", + ); + } return { expectedIntegrity: required("--integrity"), label: required("--label"), @@ -388,6 +635,12 @@ if (isMainModule()) { if (options.mode === "cache") { const verified = verifyReviewedNpmCache(options); process.stdout.write(`Verified ${verified.length} locked npm cache archives\n`); + } else if (options.mode === "installed-lock") { + const verified = verifyInstalledNpmLock(options); + process.stdout.write(`Verified ${verified.length} installed npm package identities\n`); + } else if (options.mode === "lock") { + const verified = verifyReviewedNpmLock(options); + process.stdout.write(`Verified ${verified.length} locked npm packages\n`); } else if (options.verifyOnly) { verifyReviewedNpmMetadata(options); } else { diff --git a/src/lib/sandbox/build-context.ts b/src/lib/sandbox/build-context.ts index 8780d02ddd5..7e131dfdc63 100644 --- a/src/lib/sandbox/build-context.ts +++ b/src/lib/sandbox/build-context.ts @@ -41,7 +41,7 @@ function normalizeReadModesForDockerCopy(rootDir: string): void { } function stageOpenClawRuntimeGraphs(rootDir: string, buildCtx: string): void { - for (const runtimeName of ["mcporter-runtime", "wechat-runtime"]) { + for (const runtimeName of ["mcporter-runtime", "openclaw-runtime", "wechat-runtime"]) { const sourceDir = path.join(rootDir, "agents", "openclaw", runtimeName); const stagedDir = path.join(buildCtx, "agents", "openclaw", runtimeName); fs.mkdirSync(stagedDir, { recursive: true }); diff --git a/test/fetch-guard-patch-regression.test.ts b/test/fetch-guard-patch-regression.test.ts index d6ed5bd58e1..87e18b1a600 100644 --- a/test/fetch-guard-patch-regression.test.ts +++ b/test/fetch-guard-patch-regression.test.ts @@ -142,6 +142,7 @@ function runOpenClawUpgradeBlock(currentVersion: string) { const blueprint = path.join(tmp, "blueprint.yaml"); const log = path.join(tmp, "calls.log"); const openclawInstall = path.join(tmp, "openclaw-global"); + const openclawRuntime = path.join(tmp, "openclaw-runtime"); const openclawShim = path.join(tmp, "openclaw-bin"); const mcporterInstall = path.join(tmp, "mcporter-runtime"); const mcporterShim = path.join(tmp, "mcporter-bin"); @@ -160,9 +161,21 @@ function runOpenClawUpgradeBlock(currentVersion: string) { ); fs.writeFileSync(blueprint, `min_openclaw_version: "${readBlueprintMinOpenClawVersion()}"\n`); fs.mkdirSync(openclawInstall, { recursive: true }); + fs.mkdirSync(openclawRuntime, { recursive: true }); fs.mkdirSync(mcporterInstall, { recursive: true }); fs.mkdirSync(reviewedArchiveDir); fs.writeFileSync(path.join(mcporterInstall, "package-lock.json"), "{}"); + fs.copyFileSync( + path.join( + import.meta.dirname, + "..", + "agents", + "openclaw", + "openclaw-runtime", + "package-lock.json", + ), + path.join(openclawRuntime, "package-lock.json"), + ); fs.writeFileSync(openclawShim, ""); fs.writeFileSync(mcporterShim, ""); fs.writeFileSync(auditExceptions, '{"schemaVersion":1,"exceptions":[]}\n'); @@ -173,6 +186,11 @@ function runOpenClawUpgradeBlock(currentVersion: string) { ) .replaceAll("/opt/nemoclaw-blueprint/blueprint.yaml", blueprint) .replaceAll("/usr/local/lib/node_modules/openclaw", openclawInstall) + .replaceAll( + "mkdir -p /usr/local/lib/node_modules", + `mkdir -p ${JSON.stringify(path.dirname(openclawInstall))}`, + ) + .replaceAll("/usr/local/lib/nemoclaw/openclaw-runtime", openclawRuntime) .replaceAll("/usr/local/bin/openclaw", openclawShim) .replaceAll("/usr/local/lib/node_modules/mcporter", mcporterInstall) .replaceAll("/usr/local/lib/nemoclaw/mcporter-runtime", mcporterInstall) @@ -189,7 +207,7 @@ function runOpenClawUpgradeBlock(currentVersion: string) { `real_node=${JSON.stringify(process.execPath)}`, `audit_exceptions=${JSON.stringify(auditExceptions)}`, `mcporter_install=${JSON.stringify(mcporterInstall)}`, - `postinstall_path=${JSON.stringify(path.join(openclawInstall, "scripts/postinstall-bundled-plugins.mjs"))}`, + `postinstall_path=${JSON.stringify(path.join(openclawRuntime, "node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs"))}`, `reviewed_archive=${JSON.stringify(reviewedArchive)}`, `OPENCLAW_VERSION=${JSON.stringify(openclawVersion)}`, `BASE_IMAGE=${JSON.stringify("registry.example/nemoclaw-test-base:latest")}`, @@ -210,6 +228,7 @@ function runOpenClawUpgradeBlock(currentVersion: string) { ' printf "node %s\\n" "$*" >> "$call_log"; return 0;', " fi", ' if [ "${2:-}" = "/scripts/lib/reviewed-npm-archive.mts" ]; then', + ' if [ "${3:-}" = "--verify-lock" ] || [ "${3:-}" = "--verify-installed-lock" ]; then return 0; fi', ' if [ "${3:-}" = "--verify-only" ]; then', ' [ "$#" -eq 11 ] && [ "${4:-}" = "--package-spec" ] && [ "${5:-}" = "mcporter@${MCPORTER_VERSION}" ] || return 91;', ' [ "${6:-}" = "--integrity" ] && [ "${7:-}" = "$MCPORTER_0_7_3_INTEGRITY" ] || return 92;', @@ -352,38 +371,30 @@ describe("fetch-guard patch regression guard", () => { expect(fs.existsSync(inspectMarker)).toBe(true); }); - it("installs the reviewed archive for stale and same-version OpenClaw bases", () => { + it("installs the reviewed locked graph for stale and same-version OpenClaw bases", () => { const stale = runOpenClawUpgradeBlock("2026.3.11"); expect(stale.result.status, stale.result.stderr).toBe(0); expect(stale.result.stdout).toContain( `Base image OpenClaw 2026.3.11 lacks exact reviewed provenance; installing ${CURRENT_REVIEWED_OPENCLAW_PATCH_CLASSIFIER_VERSION}`, ); - expect(stale.calls).toContain( - `npm pack https://registry.npmjs.org/openclaw/-/openclaw-${CURRENT_REVIEWED_OPENCLAW_PATCH_CLASSIFIER_VERSION}.tgz --pack-destination`, - ); - expect(stale.calls).toContain( - "npm install -g --no-audit --no-fund --no-progress --ignore-scripts ", + expect(stale.calls).toMatch( + /npm --prefix \S+\/openclaw-runtime ci --ignore-scripts --omit=dev --no-audit --no-fund --no-progress/, ); expect(stale.calls).toContain("postinstall-bundled-plugins.mjs"); - expect(stale.calls).toContain( - `openclaw-${CURRENT_REVIEWED_OPENCLAW_PATCH_CLASSIFIER_VERSION}.tgz`, - ); + expect(stale.calls).not.toContain("npm install -g"); + expect(stale.calls).not.toContain("npm pack"); const current = runOpenClawUpgradeBlock(CURRENT_REVIEWED_OPENCLAW_PATCH_CLASSIFIER_VERSION); expect(current.result.status, current.result.stderr).toBe(0); expect(current.result.stdout).toContain( `Base image OpenClaw ${CURRENT_REVIEWED_OPENCLAW_PATCH_CLASSIFIER_VERSION} lacks exact reviewed provenance; installing ${CURRENT_REVIEWED_OPENCLAW_PATCH_CLASSIFIER_VERSION}`, ); - expect(current.calls).toContain( - `npm pack https://registry.npmjs.org/openclaw/-/openclaw-${CURRENT_REVIEWED_OPENCLAW_PATCH_CLASSIFIER_VERSION}.tgz --pack-destination`, - ); - expect(current.calls).toContain( - "npm install -g --no-audit --no-fund --no-progress --ignore-scripts ", + expect(current.calls).toMatch( + /npm --prefix \S+\/openclaw-runtime ci --ignore-scripts --omit=dev --no-audit --no-fund --no-progress/, ); expect(current.calls).toContain("postinstall-bundled-plugins.mjs"); - expect(current.calls).toContain( - `openclaw-${CURRENT_REVIEWED_OPENCLAW_PATCH_CLASSIFIER_VERSION}.tgz`, - ); + expect(current.calls).not.toContain("npm install -g"); + expect(current.calls).not.toContain("npm pack"); const newer = runOpenClawUpgradeBlock("2026.7.2"); expect(newer.result.status).toBe(1); diff --git a/test/openclaw-final-image-layout.test.ts b/test/openclaw-final-image-layout.test.ts index fc2ac4acd14..7fe8ec8ef5b 100644 --- a/test/openclaw-final-image-layout.test.ts +++ b/test/openclaw-final-image-layout.test.ts @@ -18,7 +18,7 @@ describe("OpenClaw final image layout", () => { const payloads = [ { stage: "openclaw-dependency-payload", - copies: 9, + copies: 11, metadata: "/ /usr /usr/local /usr/local/lib", }, { diff --git a/test/openclaw-integrity-pin-suite.ts b/test/openclaw-integrity-pin-suite.ts index 28945b1c1ab..69ceeaf77c2 100644 --- a/test/openclaw-integrity-pin-suite.ts +++ b/test/openclaw-integrity-pin-suite.ts @@ -46,6 +46,16 @@ const PINNED_OPENCLAW_VERSION = "2026.7.1"; const PINNED_OPENCLAW_INTEGRITY = "sha512-ge/Xss99CHAjPL/ikmH/UFoiOrjcxDB4sW3y9mhyCD+dYW3wzV7TKbAVdkrXFgAG2d2BjpJofP97zUZ+umxo8g=="; const PINNED_OPENCLAW_TARBALL = "https://registry.npmjs.org/openclaw/-/openclaw-2026.7.1.tgz"; +const OPENCLAW_RUNTIME_LOCKFILE = path.join( + REPO_ROOT, + "agents", + "openclaw", + "openclaw-runtime", + "package-lock.json", +); +const PINNED_OPENCLAW_LOCK_SHA256 = createHash("sha256") + .update(fs.readFileSync(OPENCLAW_RUNTIME_LOCKFILE)) + .digest("hex"); const PINNED_NEMOCLAW_TAR_VERSION = "7.5.20"; const PINNED_NEMOCLAW_TAR_INTEGRITY = "sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ=="; @@ -117,15 +127,20 @@ function openClawBaseProvenance( status: "clean", }, ): string { + const lockSha256 = + version === PINNED_OPENCLAW_VERSION ? PINNED_OPENCLAW_LOCK_SHA256 : "none-legacy-fixture"; const recipe = - version === LEGACY_REBUILD_OPENCLAW_VERSION - ? "ignore-scripts+reviewed-lifecycle+transitive-remediation-v1" - : "ignore-scripts+reviewed-lifecycle-v1"; + version === PINNED_OPENCLAW_VERSION + ? "locked-ci+reviewed-lifecycle-v2" + : version === LEGACY_REBUILD_OPENCLAW_VERSION + ? "ignore-scripts+reviewed-lifecycle+transitive-remediation-v1" + : "ignore-scripts+reviewed-lifecycle-v1"; return [ "schema=3", `package=openclaw@${version}`, `integrity=${integrity}`, `tarball=${tarball}`, + `lock-sha256=${lockSha256}`, `recipe=${recipe}`, `mcporter-package=mcporter@${PINNED_MCPORTER_VERSION}`, `mcporter-integrity=${PINNED_MCPORTER_INTEGRITY}`, @@ -185,6 +200,8 @@ function runInstallBlock( baseProvenanceMetadata?: string; baseProvenanceSymlink?: boolean; auditExceptionPolicy?: string; + failOpenClawNpmCi?: boolean; + failOpenClawVerifyInstalledLock?: boolean; } = {}, ) { const { @@ -210,11 +227,16 @@ function runInstallBlock( baseProvenanceMetadata = "0:0:444", baseProvenanceSymlink = false, auditExceptionPolicy = fs.readFileSync(NPM_AUDIT_EXCEPTION_FILE, "utf-8"), + failOpenClawNpmCi = false, + failOpenClawVerifyInstalledLock = false, } = options; const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-openclaw-integrity-")); const blueprint = path.join(tmp, "blueprint.yaml"); const log = path.join(tmp, "calls.log"); const provenancePath = path.join(tmp, "openclaw-base-provenance-v1"); + const openclawRuntime = path.join(tmp, "openclaw-runtime"); + const openclawGlobal = path.join(tmp, "global", "openclaw"); + const openclawBin = path.join(tmp, "bin", "openclaw"); const mcporterRuntime = path.join(tmp, "mcporter-runtime"); const mcporterBin = path.join(tmp, "bin", "mcporter"); const reviewedNpmExecutable = path.join(tmp, "bin", "reviewed-npm-fixture"); @@ -226,7 +248,9 @@ function runInstallBlock( .update(auditExceptionPolicy) .digest("hex"); fs.mkdirSync(path.dirname(mcporterBin), { recursive: true }); + fs.mkdirSync(openclawRuntime, { recursive: true }); fs.mkdirSync(mcporterRuntime, { recursive: true }); + fs.copyFileSync(OPENCLAW_RUNTIME_LOCKFILE, path.join(openclawRuntime, "package-lock.json")); fs.copyFileSync(OPENCLAW_VERSION_EXTRACTOR, openclawVersionExtractor); fs.chmodSync(openclawVersionExtractor, 0o755); fs.copyFileSync(MCPORTER_LOCKFILE, path.join(mcporterRuntime, "package-lock.json")); @@ -338,8 +362,11 @@ function runInstallBlock( `openclaw_version_command_status=${openclawVersionCommandStatus}`, `installed_openclaw_version=${JSON.stringify(installedOpenClawVersion)}`, `installed_mcporter_version=${JSON.stringify(installedMcporterVersion)}`, + `fail_openclaw_npm_ci=${failOpenClawNpmCi ? "1" : "0"}`, + `fail_openclaw_verify_installed_lock=${failOpenClawVerifyInstalledLock ? "1" : "0"}`, "node() {", - ' if [ "${1:-}" = "/usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs" ]; then printf "node %s\\n" "$*" >> "$call_log"; return 0; fi', + ' if printf "%s\\n" "$*" | grep -q -- "--verify-installed-lock"; then printf "node %s\\n" "$*" >> "$call_log"; [ "$fail_openclaw_verify_installed_lock" = "0" ] || return 44; return 0; fi', + ` if [ "\${1:-}" = ${JSON.stringify(path.join(openclawRuntime, "node_modules", "openclaw", "scripts", "postinstall-bundled-plugins.mjs"))} ] || [ "\${1:-}" = ${JSON.stringify(path.join(openclawGlobal, "scripts", "postinstall-bundled-plugins.mjs"))} ]; then printf "node %s\\n" "$*" >> "$call_log"; return 0; fi`, ' if [ "${1:-}" = "--input-type=module" ] && [ "${2:-}" = "-e" ] && printf "%s\\n" "${3:-}" | grep -q "StreamableHTTPServerTransport"; then printf "node %s\\n" "$*" >> "$call_log"; return 0; fi', ' "$real_node" "$@"', "}", @@ -355,13 +382,15 @@ function runInstallBlock( ' command stat "$@"', "}", "sha256sum() {", + ` if [ "\${1:-}" = ${JSON.stringify(path.join(openclawRuntime, "package-lock.json"))} ]; then printf '%s %s\\n' ${JSON.stringify(PINNED_OPENCLAW_LOCK_SHA256)} "$1"; return 0; fi`, ` if [ "\${1:-}" = ${JSON.stringify(path.join(mcporterRuntime, "package-lock.json"))} ]; then printf '%s %s\\n' ${JSON.stringify(PINNED_MCPORTER_LOCK_SHA256)} "$1"; return 0; fi`, ` if [ "\${1:-}" = ${JSON.stringify(auditExceptionFile)} ]; then printf '%s %s\\n' ${JSON.stringify(auditExceptionPolicySha256)} "$1"; return 0; fi`, ' printf "unexpected sha256sum input: %s\\n" "${1:-}" >&2; return 1', "}", "npm() {", ' printf "npm %s\\n" "$*" >> "$call_log";', - ' [ "${1:-}" != "--prefix" ] || [ "${3:-}" != "ci" ] || installed_mcporter_version="$MCPORTER_VERSION"', + ` if [ "\${1:-}" = "--prefix" ] && [ "\${2:-}" = ${JSON.stringify(openclawRuntime)} ] && [ "\${3:-}" = "ci" ]; then [ "$fail_openclaw_npm_ci" = "0" ] || return 42; installed_openclaw_version="$OPENCLAW_VERSION"; return 0; fi`, + ` if [ "\${1:-}" = "--prefix" ] && [ "\${2:-}" = ${JSON.stringify(mcporterRuntime)} ] && [ "\${3:-}" = "ci" ]; then installed_mcporter_version="$MCPORTER_VERSION"; return 0; fi`, ' if [ "${1:-}" = "view" ] && [ "${3:-}" = "version" ]; then printf "%s\\n" "$OPENCLAW_VERSION"; return 0; fi', ` if [ "\${1:-}" = "view" ] && [ "\${2:-}" = "@zed-industries/codex-acp@${PINNED_CODEX_ACP_VERSION}" ] && [ "\${3:-}" = "dist.integrity" ]; then printf "%s\\n" ${JSON.stringify(codexAcpRegistryIntegrity)}; return 0; fi`, ` if [ "\${1:-}" = "view" ] && [ "\${2:-}" = "@zed-industries/codex-acp@${PINNED_CODEX_ACP_VERSION}" ] && [ "\${3:-}" = "dist.tarball" ]; then printf "%s\\n" ${JSON.stringify(codexAcpRegistryTarball)}; return 0; fi`, @@ -393,9 +422,15 @@ function runInstallBlock( .replaceAll("/opt/nemoclaw-blueprint/blueprint.yaml", blueprint) .replaceAll("/tmp/blueprint.yaml", blueprint) .replaceAll(OPENCLAW_BASE_PROVENANCE_PATH, provenancePath) + .replaceAll("/usr/local/lib/nemoclaw/openclaw-runtime", openclawRuntime) + .replaceAll("/usr/local/lib/node_modules/openclaw", openclawGlobal) + .replaceAll("/usr/local/lib/node_modules", path.dirname(openclawGlobal)) + .replaceAll("/usr/local/bin/openclaw", openclawBin) .replaceAll("/usr/local/lib/nemoclaw/mcporter-runtime", mcporterRuntime) .replaceAll("/usr/local/bin/mcporter", mcporterBin) .replaceAll("/usr/local/lib/nemoclaw/extract-semver", openclawVersionExtractor) + .replaceAll("/usr/local/lib", path.join(tmp, "usr-local-lib")) + .replaceAll("/usr/local/bin", path.join(tmp, "usr-local-bin")) .replaceAll("/scripts/lib/reviewed-npm-archive.mts", REVIEWED_NPM_ARCHIVE_HELPER) .replaceAll("/scripts/lib/openclaw-npm-remediation.mts", remediationHelper) .replaceAll("/scripts/lib/reviewed-npm-audit.mts", auditHelper) @@ -408,8 +443,23 @@ function runInstallBlock( const provenanceExists = fs.existsSync(provenancePath); const provenanceContent = provenanceExists ? fs.readFileSync(provenancePath, "utf-8") : null; const provenanceMode = provenanceExists ? fs.statSync(provenancePath).mode & 0o777 : null; + const runtimeExposed = [openclawGlobal, openclawBin].some((candidate) => { + try { + fs.lstatSync(candidate); + return true; + } catch { + return false; + } + }); fs.rmSync(tmp, { recursive: true, force: true }); - return { result, calls, provenanceExists, provenanceContent, provenanceMode }; + return { + result, + calls, + provenanceExists, + provenanceContent, + provenanceMode, + runtimeExposed, + }; } function runProductionBuildArgGuard( @@ -618,10 +668,10 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes expect(reviewNote).toContain("@openclaw/diagnostics-otel@2026.7.1"); expect(reviewNote).toContain("@openclaw/brave-plugin@2026.7.1"); expect(reviewNote).toContain("@tencent-weixin/openclaw-weixin@2.4.3"); - expect(reviewNote).toContain("`13` moderate"); + expect(reviewNote).toContain("three production-compatible boundaries"); + expect(reviewNote).toContain("Lower-severity findings remain visible"); expect(reviewNote).toContain("`0` high"); expect(reviewNote).toContain("`0` critical"); - expect(reviewNote).toContain("`823` total dependencies"); expect(reviewNote).toContain( "`dist/pipeline.runtime-*.js`, which exports `prepareSlackMessage`", ); @@ -890,14 +940,14 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes expect(production.result.status).toBe(0); expect(codexAcp.result.status).toBe(0); - expect(base.result.status).toBe(0); + expect(base.result.status, `${base.result.stdout}${base.result.stderr}`).toBe(0); expect(production.calls).toContain( `npm view openclaw@${PINNED_OPENCLAW_VERSION} dist.integrity`, ); expect(production.calls).toContain( `npm view openclaw@${PINNED_OPENCLAW_VERSION} dist.tarball`, ); - expect(production.calls).toContain( + expect(production.calls).not.toContain( `npm pack ${PINNED_OPENCLAW_TARBALL} --pack-destination`, ); expect(codexAcp.calls).toContain( @@ -907,13 +957,9 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes `npm view @zed-industries/codex-acp@${PINNED_CODEX_ACP_VERSION} dist.tarball`, ); expect(codexAcp.calls).toContain(`npm pack ${PINNED_CODEX_ACP_TARBALL} --pack-destination`); - expect(production.calls).toContain( - "npm install -g --no-audit --no-fund --no-progress --ignore-scripts ", - ); - expect(production.calls).toContain( - "node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs", - ); - expect(production.calls).toContain(`openclaw-${PINNED_OPENCLAW_VERSION}.tgz`); + expect(production.calls).toMatch(/npm --prefix \S+\/openclaw-runtime ci /u); + expect(production.calls).toContain("--verify-installed-lock"); + expect(production.calls).toContain("postinstall-bundled-plugins.mjs"); expect(codexAcp.calls).toContain( "npm install -g --no-audit --no-fund --no-progress --ignore-scripts ", ); @@ -921,12 +967,10 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes expect(base.calls).toContain(`npm view openclaw@${PINNED_OPENCLAW_VERSION} version`); expect(base.calls).toContain(`npm view openclaw@${PINNED_OPENCLAW_VERSION} dist.integrity`); expect(base.calls).toContain(`npm view openclaw@${PINNED_OPENCLAW_VERSION} dist.tarball`); - expect(base.calls).toContain(`npm pack ${PINNED_OPENCLAW_TARBALL} --pack-destination`); - expect(base.calls).toContain("npm install -g --ignore-scripts "); - expect(base.calls).toContain( - "node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs", - ); - expect(base.calls).toContain(`openclaw-${PINNED_OPENCLAW_VERSION}.tgz`); + expect(base.calls).not.toContain(`npm pack ${PINNED_OPENCLAW_TARBALL} --pack-destination`); + expect(base.calls).toMatch(/npm --prefix \S+\/openclaw-runtime ci /u); + expect(base.calls).toContain("--verify-installed-lock"); + expect(base.calls).toContain("postinstall-bundled-plugins.mjs"); expect(base.provenanceContent).toBe(openClawBaseProvenance()); expect(base.provenanceMode).toBe(0o444); }); @@ -1063,7 +1107,7 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes "wrong lifecycle recipe", { baseProvenance: openClawBaseProvenance().replace( - "recipe=ignore-scripts+reviewed-lifecycle-v1", + "recipe=locked-ci+reviewed-lifecycle-v2", "recipe=ignore-scripts-only-v1", ), }, @@ -1187,13 +1231,10 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes expect(result.stdout).toContain("lacks exact reviewed provenance"); expect(calls).toContain(`npm view openclaw@${PINNED_OPENCLAW_VERSION} dist.integrity`); expect(calls).toContain(`npm view openclaw@${PINNED_OPENCLAW_VERSION} dist.tarball`); - expect(calls).toContain(`npm pack ${PINNED_OPENCLAW_TARBALL} --pack-destination`); - expect(calls).toContain( - "npm install -g --no-audit --no-fund --no-progress --ignore-scripts ", - ); - expect(calls).toContain( - "node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs", - ); + expect(calls).not.toContain(`npm pack ${PINNED_OPENCLAW_TARBALL} --pack-destination`); + expect(calls).toMatch(/npm --prefix \S+\/openclaw-runtime ci /u); + expect(calls).toContain("--verify-installed-lock"); + expect(calls).toContain("postinstall-bundled-plugins.mjs"); expect(provenanceExists).toBe(false); }); @@ -1274,19 +1315,6 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes }); it("rejects npm pack filenames outside the fresh pack directories", () => { - const production = runInstallBlock( - extractRunBlock( - DOCKERFILE, - "# OPENCLAW_VERSION is the NemoClaw runtime build target", - "# Patch OpenClaw media fetch", - ), - { - openclawVersion: PINNED_OPENCLAW_VERSION, - committedIntegrity: PINNED_OPENCLAW_INTEGRITY, - registryIntegrity: PINNED_OPENCLAW_INTEGRITY, - packFilename: "../openclaw-2026.7.1.tgz", - }, - ); const codexAcp = runInstallBlock( extractRunBlock( DOCKERFILE, @@ -1307,10 +1335,13 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes "# Baseline health check.", ), { - openclawVersion: PINNED_OPENCLAW_VERSION, - committedIntegrity: PINNED_OPENCLAW_INTEGRITY, - registryIntegrity: PINNED_OPENCLAW_INTEGRITY, - packFilename: "../openclaw-2026.7.1.tgz", + openclawVersion: LEGACY_GATEWAY_UPGRADE_OPENCLAW_VERSION, + committedIntegrity: LEGACY_GATEWAY_UPGRADE_OPENCLAW_INTEGRITY, + registryIntegrity: LEGACY_GATEWAY_UPGRADE_OPENCLAW_INTEGRITY, + registryTarball: LEGACY_GATEWAY_UPGRADE_OPENCLAW_TARBALL, + packIntegrity: LEGACY_GATEWAY_UPGRADE_OPENCLAW_INTEGRITY, + allowLegacyFixture: true, + packFilename: "../openclaw-2026.4.24.tgz", }, ); const optionalPlugin = runOptionalOpenClawPluginBlock({ @@ -1318,12 +1349,6 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes }); for (const item of [ - { - label: "production Dockerfile", - outcome: production, - unsafeFilename: "../openclaw-2026.7.1.tgz", - blockedCommand: "npm install -g", - }, { label: "codex-acp Dockerfile", outcome: codexAcp, @@ -1333,7 +1358,7 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes { label: "base Dockerfile", outcome: base, - unsafeFilename: "../openclaw-2026.7.1.tgz", + unsafeFilename: "../openclaw-2026.4.24.tgz", blockedCommand: "npm install -g", }, { @@ -1361,18 +1386,23 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes "# Baseline health check.", ), { - openclawVersion: PINNED_OPENCLAW_VERSION, - committedIntegrity: PINNED_OPENCLAW_INTEGRITY, - registryIntegrity: PINNED_OPENCLAW_INTEGRITY, + openclawVersion: LEGACY_GATEWAY_UPGRADE_OPENCLAW_VERSION, + committedIntegrity: LEGACY_GATEWAY_UPGRADE_OPENCLAW_INTEGRITY, + registryIntegrity: LEGACY_GATEWAY_UPGRADE_OPENCLAW_INTEGRITY, + registryTarball: LEGACY_GATEWAY_UPGRADE_OPENCLAW_TARBALL, + packIntegrity: LEGACY_GATEWAY_UPGRADE_OPENCLAW_INTEGRITY, + allowLegacyFixture: true, packFilename: null, }, ); - const diagnostic = `npm pack openclaw@${PINNED_OPENCLAW_VERSION} did not report filename and integrity`; + const diagnostic = `npm pack openclaw@${LEGACY_GATEWAY_UPGRADE_OPENCLAW_VERSION} did not report filename and integrity`; expect(result.status).not.toBe(0); expect(result.stderr).toContain(diagnostic); expect(result.stdout).not.toContain(diagnostic); - expect(calls).toContain(`npm pack ${PINNED_OPENCLAW_TARBALL} --pack-destination`); + expect(calls).toContain( + `npm pack ${LEGACY_GATEWAY_UPGRADE_OPENCLAW_TARBALL} --pack-destination`, + ); expect(calls).not.toContain("npm install -g"); }); } @@ -1464,9 +1494,7 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes expect(fixtureBase.calls).not.toContain("postinstall-bundled-plugins.mjs"); expect(gatewayFixtureBase.result.status).toBe(0); expect(gatewayFixtureBase.calls).toContain("npm install -g --ignore-scripts "); - expect(gatewayFixtureBase.calls).toContain( - "node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs", - ); + expect(gatewayFixtureBase.calls).toContain("postinstall-bundled-plugins.mjs"); }); it("guards production Docker build args from legacy OpenClaw fixture inputs", () => { @@ -1621,8 +1649,8 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes } }); - it("fails closed before npm install when the downloaded OpenClaw tarball integrity drifts", () => { - const { result, calls } = runInstallBlock( + it("leaves no runtime exposure when npm ci rejects downloaded OpenClaw bytes", () => { + const outcome = runInstallBlock( extractRunBlock( DOCKERFILE, "# OPENCLAW_VERSION is the NemoClaw runtime build target", @@ -1632,19 +1660,50 @@ export function registerOpenClawIntegrityPinTests(group: OpenClawIntegrityPinTes openclawVersion: PINNED_OPENCLAW_VERSION, committedIntegrity: PINNED_OPENCLAW_INTEGRITY, registryIntegrity: PINNED_OPENCLAW_INTEGRITY, - packIntegrity: "sha512-downloaded-drift", + failOpenClawNpmCi: true, }, ); - const output = `${result.stdout}${result.stderr}`; - expect(result.status).not.toBe(0); - expect(output).toContain( - `OpenClaw ${PINNED_OPENCLAW_VERSION} downloaded tarball integrity mismatch`, - ); - expect(output).toContain(`Expected: ${PINNED_OPENCLAW_INTEGRITY}`); - expect(output).toContain("Actual: sha512-downloaded-drift"); - expect(calls).toContain(`npm pack ${PINNED_OPENCLAW_TARBALL} --pack-destination`); - expect(calls).not.toContain("npm install -g"); + expect(outcome.result.status).not.toBe(0); + expect(outcome.calls).toMatch(/npm --prefix \S+\/openclaw-runtime ci /u); + expect(outcome.calls).not.toContain("postinstall-bundled-plugins.mjs"); + expect(outcome.runtimeExposed).toBe(false); + expect(outcome.provenanceExists).toBe(false); + }); + + it("leaves no runtime exposure when installed-lock verification fails", () => { + const installBlocks = [ + { + label: "production Dockerfile", + file: DOCKERFILE, + startMarker: "# OPENCLAW_VERSION is the NemoClaw runtime build target", + endMarker: "# Patch OpenClaw media fetch", + }, + { + label: "base Dockerfile", + file: DOCKERFILE_BASE, + startMarker: "# Install OpenClaw CLI + PyYAML.", + endMarker: "# Baseline health check.", + }, + ]; + + for (const block of installBlocks) { + const outcome = runInstallBlock( + extractRunBlock(block.file, block.startMarker, block.endMarker), + { + openclawVersion: PINNED_OPENCLAW_VERSION, + committedIntegrity: PINNED_OPENCLAW_INTEGRITY, + registryIntegrity: PINNED_OPENCLAW_INTEGRITY, + failOpenClawVerifyInstalledLock: true, + }, + ); + + expect(outcome.result.status, block.label).not.toBe(0); + expect(outcome.calls, block.label).toContain("--verify-installed-lock"); + expect(outcome.calls, block.label).not.toContain("postinstall-bundled-plugins.mjs"); + expect(outcome.runtimeExposed, block.label).toBe(false); + expect(outcome.provenanceExists, block.label).toBe(false); + } }); it("fails closed before npm install for unpinned production Dockerfile overrides", () => { diff --git a/test/openclaw-lifecycle-policy.test.ts b/test/openclaw-lifecycle-policy.test.ts index 2123034d2cb..fdd11812769 100644 --- a/test/openclaw-lifecycle-policy.test.ts +++ b/test/openclaw-lifecycle-policy.test.ts @@ -23,14 +23,29 @@ function corePackageSpecs(block) { } function explicitLifecycleScripts(block) { - return [...block.matchAll( + const scripts = [...block.matchAll( /^\s*([0-9]+(?:\.[0-9]+){2}(?:\|[0-9]+(?:\.[0-9]+){2})*)\)\s+(node [^;]+postinstall-bundled-plugins\.mjs)\s+;;/gm, )].flatMap((match) => match[1].split("|").map((version) => ({ packageSpec: "openclaw@" + version, explicitCommand: match[2], })), - ).sort((left, right) => left.packageSpec.localeCompare(right.packageSpec)); + ); + const lockedRuntimeCommand = + "node /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs"; + if ( + block.includes("npm --prefix /usr/local/lib/nemoclaw/openclaw-runtime ci") && + block.includes(lockedRuntimeCommand) + ) { + const manifest = JSON.parse( + fs.readFileSync("agents/openclaw/openclaw-runtime/package.json", "utf8"), + ); + scripts.push({ + packageSpec: "openclaw@" + manifest.dependencies.openclaw, + explicitCommand: lockedRuntimeCommand, + }); + } + return scripts.sort((left, right) => left.packageSpec.localeCompare(right.packageSpec)); } const dockerfile = fs.readFileSync("Dockerfile", "utf8"); diff --git a/test/openclaw-locked-install.test.ts b/test/openclaw-locked-install.test.ts new file mode 100644 index 00000000000..5fd4506c3a4 --- /dev/null +++ b/test/openclaw-locked-install.test.ts @@ -0,0 +1,389 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { createHash } from "node:crypto"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; +import { + type ReviewedNpmArchiveRequest, + verifyInstalledNpmLock, + verifyReviewedNpmLock, +} from "../scripts/lib/reviewed-npm-archive.mts"; + +const REPO_ROOT = path.join(import.meta.dirname, ".."); +const RUNTIME_DIRECTORY = path.join(REPO_ROOT, "agents", "openclaw", "openclaw-runtime"); +const LOCKFILE = path.join(RUNTIME_DIRECTORY, "package-lock.json"); +const PACKAGE_SPEC = "openclaw@2026.7.1"; +const INTEGRITY = + "sha512-ge/Xss99CHAjPL/ikmH/UFoiOrjcxDB4sW3y9mhyCD+dYW3wzV7TKbAVdkrXFgAG2d2BjpJofP97zUZ+umxo8g=="; +const TARBALL = "https://registry.npmjs.org/openclaw/-/openclaw-2026.7.1.tgz"; +const LOCK_SHA256 = "82489f62febb12da52833c0b1f7f6969f7e21a098c565ef1f91342b1e5e32d88"; +const roots: string[] = []; + +function sha256(file: string): string { + return createHash("sha256").update(fs.readFileSync(file)).digest("hex"); +} + +function lockRequest(lockfilePath = LOCKFILE, expectedLockSha256 = LOCK_SHA256) { + return { + expectedIntegrity: INTEGRITY, + expectedLockSha256, + label: "OpenClaw 2026.7.1 locked runtime graph", + lockfilePath, + packageSpec: PACKAGE_SPEC, + registryOrigin: "https://registry.npmjs.org/", + tarballUrl: TARBALL, + }; +} + +function reviewedMetadata(args: readonly string[], request: ReviewedNpmArchiveRequest): string { + return args[2] === "dist.integrity" ? request.expectedIntegrity : request.tarballUrl; +} + +function mutatedLock(mutate: (lock: any) => void): string { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-openclaw-lock-test-")); + roots.push(root); + const target = path.join(root, "package-lock.json"); + const lock = JSON.parse(fs.readFileSync(LOCKFILE, "utf-8")); + mutate(lock); + fs.writeFileSync(target, `${JSON.stringify(lock, null, 2)}\n`); + return target; +} + +type InstalledFixtureLayout = + | "dangling-package-symlink" + | "manifest-symlink" + | "omitted" + | "package-symlink" + | "regular"; + +type InstalledFixtureWriter = (args: { + readonly actualName: string; + readonly actualVersion: string; + readonly packageDirectory: string; + readonly root: string; +}) => void; + +function writePackageManifest( + packageDirectory: string, + actualName: string, + actualVersion: string, +): void { + fs.mkdirSync(packageDirectory, { recursive: true }); + fs.writeFileSync( + path.join(packageDirectory, "package.json"), + JSON.stringify({ name: actualName, version: actualVersion }), + ); +} + +const INSTALLED_FIXTURE_WRITERS: Readonly> = + { + "dangling-package-symlink": ({ packageDirectory, root }) => { + fs.mkdirSync(path.dirname(packageDirectory), { recursive: true }); + fs.symlinkSync(path.join(root, "substituted-package"), packageDirectory); + }, + "manifest-symlink": ({ actualName, actualVersion, packageDirectory, root }) => { + fs.mkdirSync(packageDirectory, { recursive: true }); + const target = path.join(root, "substituted-package.json"); + fs.writeFileSync(target, JSON.stringify({ name: actualName, version: actualVersion })); + fs.symlinkSync(target, path.join(packageDirectory, "package.json")); + }, + omitted: () => undefined, + "package-symlink": ({ actualName, actualVersion, packageDirectory, root }) => { + const target = path.join(root, "substituted-package"); + writePackageManifest(target, actualName, actualVersion); + fs.mkdirSync(path.dirname(packageDirectory), { recursive: true }); + fs.symlinkSync(target, packageDirectory); + }, + regular: ({ actualName, actualVersion, packageDirectory }) => { + writePackageManifest(packageDirectory, actualName, actualVersion); + }, + }; + +function installedFixture({ + actualName = "chalk", + actualVersion = "5.6.2", + lockedName, + danglingSymlink = false, + manifestSymlink = false, + omit = false, + optional = false, + symlink = false, +}: { + actualName?: string; + actualVersion?: string; + lockedName?: string; + danglingSymlink?: boolean; + manifestSymlink?: boolean; + omit?: boolean; + optional?: boolean; + symlink?: boolean; +} = {}) { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-installed-lock-test-")); + roots.push(root); + const installRoot = path.join(root, "runtime"); + const lockfilePath = path.join(root, "package-lock.json"); + const packageDirectory = path.join(installRoot, "node_modules", "chalk"); + const lock = { + lockfileVersion: 3, + packages: { + "": { dependencies: { chalk: "5.6.2" } }, + "node_modules/chalk": { + integrity: `sha512-${"D".repeat(88)}`, + ...(lockedName ? { name: lockedName } : {}), + optional, + resolved: "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + version: "5.6.2", + }, + }, + }; + fs.writeFileSync(lockfilePath, `${JSON.stringify(lock, null, 2)}\n`); + const layout: InstalledFixtureLayout = omit + ? "omitted" + : symlink + ? danglingSymlink + ? "dangling-package-symlink" + : "package-symlink" + : manifestSymlink + ? "manifest-symlink" + : "regular"; + INSTALLED_FIXTURE_WRITERS[layout]({ + actualName, + actualVersion, + packageDirectory, + root, + }); + return { + expectedLockSha256: sha256(lockfilePath), + installRoot, + label: "test locked graph", + lockfilePath, + }; +} + +afterEach(() => { + for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); +}); + +describe("locked OpenClaw production installation (#5896)", () => { + it("binds the reviewed root artifact to the complete committed closure", () => { + const verified = verifyReviewedNpmLock(lockRequest(), reviewedMetadata); + expect(verified).toHaveLength(307); + expect(verified).toContain(PACKAGE_SPEC); + expect(verified).toContain("brace-expansion@5.0.8"); + expect(verified).toContain("fast-uri@3.1.4"); + expect(sha256(LOCKFILE)).toBe(LOCK_SHA256); + }); + + // source-shape-contract: security -- The committed production lock digest must fail before any registry-controlled metadata is consulted + it("rejects any lock byte tamper before registry metadata is consulted", () => { + const lockfilePath = mutatedLock((lock) => { + lock.packages["node_modules/openclaw/node_modules/chalk"].integrity = + `sha512-${"A".repeat(88)}`; + }); + let npmCalled = false; + expect(() => + verifyReviewedNpmLock(lockRequest(lockfilePath), () => { + npmCalled = true; + return ""; + }), + ).toThrow("lock SHA-256 mismatch"); + expect(npmCalled).toBe(false); + }); + + // source-shape-contract: security -- Mutating the shipped lock proves every reviewed transitive identity remains bound to committed production bytes + it.each([ + { + expected: "root must depend only on openclaw@2026.7.1", + mutate: (lock: any) => { + lock.packages[""].dependencies.openclaw = "2026.7.2"; + }, + name: "root version drift", + }, + { + expected: "root must depend only on openclaw@2026.7.1", + mutate: (lock: any) => { + lock.packages[""].optionalDependencies = { "left-pad": "1.3.0" }; + }, + name: "root optional dependency injection", + }, + { + expected: "lock integrity mismatch for openclaw@2026.7.1", + mutate: (lock: any) => { + lock.packages["node_modules/openclaw"].integrity = `sha512-${"B".repeat(88)}`; + }, + name: "top-level integrity drift", + }, + { + expected: "nested shrinkwrap delegation is not allowed", + mutate: (lock: any) => { + lock.packages["node_modules/openclaw"].hasShrinkwrap = true; + }, + name: "nested shrinkwrap delegation", + }, + { + expected: "must use a committed sha512 npm integrity value", + mutate: (lock: any) => { + delete lock.packages["node_modules/openclaw/node_modules/chalk"].integrity; + }, + name: "missing transitive integrity", + }, + { + expected: "must use the reviewed registry", + mutate: (lock: any) => { + lock.packages["node_modules/openclaw/node_modules/chalk"].resolved = + "https://packages.invalid/chalk-5.6.2.tgz"; + }, + name: "malicious transitive registry substitution", + }, + { + expected: "conflicting package identity: safe-buffer@5.1.2", + mutate: (lock: any) => { + lock.packages[ + "node_modules/openclaw/node_modules/string_decoder/node_modules/safe-buffer" + ].integrity = `sha512-${"C".repeat(88)}`; + }, + name: "conflicting duplicate package identity", + }, + ])("rejects $name even with a test-only matching lock digest", ({ expected, mutate }) => { + const lockfilePath = mutatedLock(mutate); + expect(() => + verifyReviewedNpmLock(lockRequest(lockfilePath, sha256(lockfilePath)), reviewedMetadata), + ).toThrow(expected); + }); + + it("binds installed package manifests to lock locations and versions", () => { + expect(verifyInstalledNpmLock(installedFixture())).toEqual(["chalk@5.6.2"]); + }); + + it("binds npm aliases to the canonical package name recorded in the lock", () => { + expect( + verifyInstalledNpmLock( + installedFixture({ + actualName: "@scope/canonical", + lockedName: "@scope/canonical", + }), + ), + ).toEqual(["@scope/canonical@5.6.2"]); + }); + + // source-shape-contract: security -- The installed production graph must reject package identity substitution after a reviewed tarball is extracted + it("rejects a same-registry tarball with a substituted package manifest", () => { + expect(() => + verifyInstalledNpmLock(installedFixture({ actualName: "is-odd", actualVersion: "3.0.1" })), + ).toThrow("expected chalk@5.6.2, found is-odd@3.0.1"); + }); + + // source-shape-contract: security -- Production lock verification must fail closed when required package locations are absent or redirected through symlinks + it("fails closed on missing required packages and symlinked package roots", () => { + expect(() => verifyInstalledNpmLock(installedFixture({ omit: true }))).toThrow( + "missing installed package: chalk@5.6.2", + ); + expect(() => verifyInstalledNpmLock(installedFixture({ symlink: true }))).toThrow( + "installed package must be a non-symlink directory", + ); + expect(() => + verifyInstalledNpmLock( + installedFixture({ + danglingSymlink: true, + optional: true, + symlink: true, + }), + ), + ).toThrow("installed package must be a non-symlink directory"); + }); + + // source-shape-contract: security -- Installed production manifests must remain regular files beneath their reviewed package locations + it("rejects symlinked package manifests", () => { + expect(() => verifyInstalledNpmLock(installedFixture({ manifestSymlink: true }))).toThrow( + "manifest must be a non-symlink regular file", + ); + }); + + it("allows npm to omit an incompatible optional package", () => { + expect(verifyInstalledNpmLock(installedFixture({ omit: true, optional: true }))).toEqual([]); + }); + + // source-shape-contract: compatibility -- Both shipped Dockerfiles must preserve the reviewed lock verification and installation sequence + it.each([ + "Dockerfile", + "Dockerfile.base", + ])("invokes the locked installer before exposing OpenClaw in %s", (dockerfileName) => { + const contents = fs.readFileSync(path.join(REPO_ROOT, dockerfileName), "utf-8"); + const verifyIndex = contents.indexOf( + "node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts --verify-lock", + ); + const installIndex = contents.indexOf( + "npm --prefix /usr/local/lib/nemoclaw/openclaw-runtime ci", + verifyIndex, + ); + const installedIdentityIndex = contents.indexOf("--verify-installed-lock", installIndex); + const postinstallIndex = contents.indexOf( + "/usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs", + installedIdentityIndex, + ); + const linkIndex = contents.indexOf( + "ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw", + postinstallIndex, + ); + const binLinkIndex = contents.indexOf( + "ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/.bin/openclaw", + linkIndex, + ); + const branchEnd = contents.indexOf("else \\", installIndex); + const currentInstallBranch = contents.slice(verifyIndex, branchEnd); + + expect(contents).toContain( + "COPY agents/openclaw/openclaw-runtime/package-lock.json /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json", + ); + expect(verifyIndex).toBeGreaterThanOrEqual(0); + expect(installIndex).toBeGreaterThan(verifyIndex); + expect(installedIdentityIndex).toBeGreaterThan(installIndex); + expect(postinstallIndex).toBeGreaterThan(installedIdentityIndex); + expect(linkIndex).toBeGreaterThan(postinstallIndex); + expect(binLinkIndex).toBeGreaterThan(linkIndex); + expect(branchEnd).toBeGreaterThan(installIndex); + expect(currentInstallBranch).toContain(`--lock-sha256 \"$OPENCLAW_LOCK_SHA256\"`); + expect(currentInstallBranch).not.toContain("npm install -g"); + expect(contents).toContain("'schema=3'"); + expect(contents).toContain('"lock-sha256=${OPENCLAW_LOCK_SHA256}"'); + expect(contents).toContain("locked-ci+reviewed-lifecycle-v2"); + }); + + // source-shape-contract: security -- The shipped audit and base-image rebuild inputs must share the exact committed production lock authority + it("audits the same lock and rebuilds the base when its graph changes", () => { + const audit = JSON.parse( + fs.readFileSync(path.join(REPO_ROOT, "ci", "reviewed-npm-audit.json"), "utf-8"), + ); + expect(audit.archivePackages).toEqual( + expect.arrayContaining([expect.objectContaining({ packageSpec: PACKAGE_SPEC })]), + ); + expect(audit.lockedGraphs).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + directory: "agents/openclaw/openclaw-runtime", + lockSha256: LOCK_SHA256, + packageSpec: PACKAGE_SPEC, + }), + ]), + ); + + const baseWorkflow = fs.readFileSync( + path.join(REPO_ROOT, ".github", "workflows", "base-image.yaml"), + "utf-8", + ); + expect(baseWorkflow).toContain('"agents/openclaw/openclaw-runtime/package.json"'); + expect(baseWorkflow).toContain('"agents/openclaw/openclaw-runtime/package-lock.json"'); + expect(baseWorkflow).toContain('"scripts/lib/reviewed-npm-archive.mts"'); + + const baseResolver = fs.readFileSync( + path.join(REPO_ROOT, ".github", "actions", "resolve-sandbox-base-image", "action.yaml"), + "utf-8", + ); + expect(baseResolver).toContain("agents/openclaw/openclaw-runtime/package.json"); + expect(baseResolver).toContain("agents/openclaw/openclaw-runtime/package-lock.json"); + expect(baseResolver).toContain("scripts/lib/reviewed-npm-archive.mts"); + }); +}); diff --git a/test/openclaw-npm-remediation.test.ts b/test/openclaw-npm-remediation.test.ts index 15d97609f04..a1cfbb3ea55 100644 --- a/test/openclaw-npm-remediation.test.ts +++ b/test/openclaw-npm-remediation.test.ts @@ -19,6 +19,7 @@ import { afterEach, describe, expect, it } from "vitest"; import { buildRemediatedOpenClawArchive, hashPackageTree, + patchCurrentOpenClawCorePackageGraph, patchLegacyOpenClawCorePackageGraph, patchOpenClawDiagnosticsOtelPackageGraph, patchOpenClawPluginPackageGraph, @@ -140,6 +141,73 @@ function writeLegacyCoreFixture(tarVersion = "7.5.11"): string { return directory; } +function writeCurrentCoreFixture( + braceExpansionVersion = "5.0.7", + fastUriVersion = "3.1.2", +): string { + const directory = mkdtempSync(path.join(tmpdir(), "nemoclaw-current-openclaw-core-remediation-")); + temporaryDirectories.push(directory); + writeFileSync( + path.join(directory, "package.json"), + `${JSON.stringify( + { + name: "openclaw", + version: "2026.7.1", + dependencies: { + "@modelcontextprotocol/sdk": "1.29.0", + minimatch: "10.2.5", + }, + }, + null, + 2, + )}\n`, + ); + writeFileSync( + path.join(directory, "npm-shrinkwrap.json"), + `${JSON.stringify( + { + name: "openclaw", + version: "2026.7.1", + lockfileVersion: 3, + packages: { + "": { + name: "openclaw", + version: "2026.7.1", + dependencies: { + "@modelcontextprotocol/sdk": "1.29.0", + minimatch: "10.2.5", + }, + }, + "node_modules/ajv": { + version: "8.20.0", + dependencies: { "fast-uri": "^3.0.1" }, + }, + "node_modules/brace-expansion": { + version: braceExpansionVersion, + resolved: `https://registry.npmjs.org/brace-expansion/-/brace-expansion-${braceExpansionVersion}.tgz`, + integrity: + "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + dependencies: { "balanced-match": "^4.0.2" }, + }, + "node_modules/fast-uri": { + version: fastUriVersion, + resolved: `https://registry.npmjs.org/fast-uri/-/fast-uri-${fastUriVersion}.tgz`, + integrity: + "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + }, + "node_modules/minimatch": { + version: "10.2.5", + dependencies: { "brace-expansion": "^5.0.5" }, + }, + }, + }, + null, + 2, + )}\n`, + ); + return directory; +} + function readJson(file: string): T { return JSON.parse(readFileSync(file, "utf-8")) as T; } @@ -351,6 +419,40 @@ describe("OpenClaw npm remediation", () => { ); }); + it("replaces the reviewed current OpenClaw transitive resolutions", () => { + const directory = writeCurrentCoreFixture(); + + patchCurrentOpenClawCorePackageGraph(directory); + + const shrinkwrap = readJson<{ + packages: Record; + }>(path.join(directory, "npm-shrinkwrap.json")); + expect(shrinkwrap.packages["node_modules/brace-expansion"]).toMatchObject({ + version: "5.0.8", + resolved: "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + integrity: + "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + }); + expect(shrinkwrap.packages["node_modules/fast-uri"]).toMatchObject({ + version: "3.1.4", + resolved: "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + integrity: + "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + }); + }); + + it.each([ + ["brace-expansion", "5.0.6", "brace-expansion layout changed after review"], + ["fast-uri", "3.1.1", "fast-uri layout changed after review"], + ])("rejects a current OpenClaw %s graph that changed after review", (dependency, version, error) => { + const directory = + dependency === "brace-expansion" + ? writeCurrentCoreFixture(version) + : writeCurrentCoreFixture("5.0.7", version); + + expect(() => patchCurrentOpenClawCorePackageGraph(directory)).toThrow(error); + }); + it("rebuilds the legacy fixture archive with the reviewed tar package bundled", () => { const fixture = writeLegacyCoreArchiveFixtures(); const request = { diff --git a/test/reviewed-npm-archive.test.ts b/test/reviewed-npm-archive.test.ts index 9040fa37891..6af09f1ddaa 100644 --- a/test/reviewed-npm-archive.test.ts +++ b/test/reviewed-npm-archive.test.ts @@ -201,6 +201,40 @@ describe("reviewed npm archive", () => { } }); + it("uses a lock alias's canonical package identity for cache verification", () => { + const reviewed = cacheRequest(); + const lockfilePath = path.join(reviewed.tempDirectory as string, "alias-lock.json"); + fs.writeFileSync( + lockfilePath, + `${JSON.stringify( + { + lockfileVersion: 3, + packages: { + "": {}, + "node_modules/legacy-name": { + integrity: INTEGRITY, + name: "@example/reviewed", + resolved: TARBALL_URL, + version: "1.2.3", + }, + }, + }, + null, + 2, + )}\n`, + ); + const calls: Array<{ args: readonly string[]; request: ReviewedNpmArchiveRequest }> = []; + + expect( + verifyReviewedNpmCache({ ...reviewed, lockfilePath }, cachedArchiveRunner(calls)), + ).toEqual([PACKAGE_SPEC]); + expect(calls.map(({ request }) => request.packageSpec)).toEqual([ + PACKAGE_SPEC, + PACKAGE_SPEC, + PACKAGE_SPEC, + ]); + }); + it("rejects an off-origin locked archive before npm can read the cache", () => { const reviewed = cacheRequest(); const lock = JSON.parse(fs.readFileSync(WECHAT_LOCK, "utf-8")); diff --git a/test/reviewed-npm-audit-workflow.test.ts b/test/reviewed-npm-audit-workflow.test.ts new file mode 100644 index 00000000000..e69328126b3 --- /dev/null +++ b/test/reviewed-npm-audit-workflow.test.ts @@ -0,0 +1,208 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import fs from "node:fs"; +import { createRequire } from "node:module"; +import os from "node:os"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { normalizeOpenClawSignatureAlias } from "../scripts/audit-reviewed-npm-graph.mts"; +import { readYaml } from "./helpers/e2e-workflow-contract"; + +type WorkflowStep = { + readonly env?: Record; + readonly id?: string; + readonly if?: string; + readonly name?: string; + readonly run?: string; + readonly uses?: string; + readonly with?: Record; +}; + +type WorkflowJob = { + readonly needs?: string | readonly string[]; + readonly steps?: readonly WorkflowStep[]; +}; + +type Workflow = { + readonly jobs: Record; +}; + +const REPO_ROOT = path.join(import.meta.dirname, ".."); +const BOOTSTRAP_SHA = "0c7dd29394d2c4db660c4d09f3654c0789e200d0"; +// Removal condition: delete the PR-6830 fork bootstrap after this PR merges and +// the base branch contains the schema-v2 reviewed npm audit action. +const BOOTSTRAP_IF = + "${{ steps.trusted-reviewed-npm-audit.outputs.available != 'true' && github.event.pull_request.number == 6830 && github.event.pull_request.head.repo.full_name == 'HOYALIM/NemoClaw' }}"; +const REJECT_UNAVAILABLE_IF = + "${{ steps.trusted-reviewed-npm-audit.outputs.available != 'true' && (github.event.pull_request.number != 6830 || github.event.pull_request.head.repo.full_name != 'HOYALIM/NemoClaw') }}"; +const DOMEXCEPTION_INTEGRITY = + "sha512-tlc/FcYIv5i8RYsl2iDil4A0gOihaas1R5jPcIC4Zw3GhjKsVilw90aHcVlhZPTBLGBzd379S+VcnsDjd9ChiA=="; + +function requiredStep(job: WorkflowJob, name: string): WorkflowStep { + const step = job.steps?.find((candidate) => candidate.name === name); + expect(step, `Missing workflow step: ${name}`).toBeDefined(); + return step as WorkflowStep; +} + +describe("trusted reviewed npm audit workflow (#5896)", () => { + // source-shape-contract: security -- PR dependency audit code must come from the base SHA or the one-time signed bootstrap + it("runs PR audits from trusted code and keeps the main audit on the checked-in action", () => { + const pr = readYaml(".github/workflows/pr.yaml"); + const main = readYaml(".github/workflows/main.yaml"); + const prJob = pr.jobs["reviewed-npm-audit"]; + const mainJob = main.jobs["reviewed-npm-audit"]; + + const trustedCheckout = requiredStep(prJob, "Checkout trusted reviewed npm audit"); + expect(trustedCheckout.with).toMatchObject({ + ref: "${{ github.event.pull_request.base.sha }}", + path: ".trusted-reviewed-npm-audit", + "persist-credentials": false, + "sparse-checkout-cone-mode": false, + }); + const sparseCheckout = String(trustedCheckout.with?.["sparse-checkout"]); + expect(sparseCheckout).toContain(".github/actions/ci-reviewed-npm-audit"); + expect(sparseCheckout).toContain("ci/npm-audit-exceptions.json"); + expect(sparseCheckout).toContain("ci/reviewed-npm-audit.json"); + expect(sparseCheckout).toContain("scripts/audit-reviewed-npm-graph.mts"); + expect(sparseCheckout).toContain("scripts/lib/openclaw-npm-remediation.mts"); + expect(sparseCheckout).toContain("scripts/lib/reviewed-npm-archive.mts"); + expect(sparseCheckout).toContain("scripts/lib/reviewed-npm-audit.mts"); + + const detection = requiredStep(prJob, "Detect trusted reviewed npm audit schema"); + expect(detection.id).toBe("trusted-reviewed-npm-audit"); + expect(detection.run).toContain("resolveTrustedAuditConfigPath(TRUSTED_REPO_ROOT)"); + expect(detection.run).toContain(".trusted-reviewed-npm-audit/ci/npm-audit-exceptions.json"); + expect(detection.run).toContain(".trusted-reviewed-npm-audit/ci/reviewed-npm-audit.json"); + expect(detection.run).toContain( + ".trusted-reviewed-npm-audit/scripts/lib/openclaw-npm-remediation.mts", + ); + expect(detection.run).toContain( + ".trusted-reviewed-npm-audit/scripts/lib/reviewed-npm-audit.mts", + ); + + const bootstrap = requiredStep(prJob, "Checkout pinned bootstrap reviewed npm audit"); + expect(bootstrap.if).toBe(BOOTSTRAP_IF); + expect(bootstrap.with).toMatchObject({ + repository: "HOYALIM/NemoClaw", + ref: BOOTSTRAP_SHA, + path: ".trusted-reviewed-npm-audit-bootstrap", + "persist-credentials": false, + }); + const bootstrapSparseCheckout = String(bootstrap.with?.["sparse-checkout"]); + expect(bootstrapSparseCheckout).toContain("ci/npm-audit-exceptions.json"); + expect(bootstrapSparseCheckout).toContain("scripts/lib/openclaw-npm-remediation.mts"); + expect(bootstrapSparseCheckout).toContain("scripts/lib/reviewed-npm-audit.mts"); + const rejectUnavailable = requiredStep(prJob, "Reject unavailable trusted reviewed npm audit"); + expect(rejectUnavailable.if).toBe(REJECT_UNAVAILABLE_IF); + expect(rejectUnavailable.run).toContain("exit 1"); + expect(requiredStep(prJob, "Audit reviewed production npm graphs")).toMatchObject({ + if: "${{ steps.trusted-reviewed-npm-audit.outputs.available == 'true' }}", + uses: "./.trusted-reviewed-npm-audit/.github/actions/ci-reviewed-npm-audit", + with: { + "target-root": "${{ github.workspace }}", + "report-dir": "artifacts/reviewed-npm-audit", + }, + }); + expect( + requiredStep(prJob, "Audit reviewed production npm graphs (pinned bootstrap)"), + ).toMatchObject({ + if: BOOTSTRAP_IF, + uses: "./.trusted-reviewed-npm-audit-bootstrap/.github/actions/ci-reviewed-npm-audit", + }); + expect(requiredStep(mainJob, "Audit reviewed production npm graphs")).toMatchObject({ + uses: "./.github/actions/ci-reviewed-npm-audit", + with: { + "target-root": "${{ github.workspace }}", + "report-dir": "artifacts/reviewed-npm-audit", + }, + }); + }); + + // source-shape-contract: security -- The trusted composite action must execute only its bundled driver while treating the PR checkout as explicit data + it("executes the trusted driver and helper against explicit target inputs", () => { + const action = fs.readFileSync( + path.join(REPO_ROOT, ".github", "actions", "ci-reviewed-npm-audit", "action.yaml"), + "utf8", + ); + const driver = fs.readFileSync( + path.join(REPO_ROOT, "scripts", "audit-reviewed-npm-graph.mts"), + "utf8", + ); + + expect(action).toContain('node-version: "22.23.1"'); + expect(action).toContain("npm install --global npm@10.9.4"); + expect(action).toContain("NEMOCLAW_REVIEWED_NPM_AUDIT_TARGET_ROOT"); + expect(action).toContain("NEMOCLAW_REVIEWED_NPM_AUDIT_REPORT_DIR"); + expect(action).toContain( + 'node --experimental-strip-types "$GITHUB_ACTION_PATH/../../../scripts/audit-reviewed-npm-graph.mts"', + ); + expect(action).not.toContain("run: node --experimental-strip-types scripts/"); + expect(driver).toContain("resolveTrustedAuditConfigPath(TRUSTED_REPO_ROOT)"); + expect(driver).not.toContain('resolveTargetPath(\n "ci/reviewed-npm-audit.json"'); + }); + + it("normalizes only the reviewed OpenClaw npm alias for registry signature verification", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-signature-alias-")); + const aliasPath = path.join("node_modules", "openclaw", "node_modules", "node-domexception"); + const actualPath = path.join( + "node_modules", + "openclaw", + "node_modules", + "@nolyfill", + "domexception", + ); + const requesterPath = path.join("node_modules", "openclaw", "node_modules", "fetch-blob"); + const aliasManifest = { name: "@nolyfill/domexception", version: "1.0.28" }; + const requesterManifest = { + name: "fetch-blob", + version: "3.2.0", + dependencies: { "node-domexception": "^1.0.0" }, + }; + const lock = { + lockfileVersion: 3, + packages: { + [aliasPath]: { + ...aliasManifest, + resolved: "https://registry.npmjs.org/@nolyfill/domexception/-/domexception-1.0.28.tgz", + integrity: DOMEXCEPTION_INTEGRITY, + }, + [requesterPath]: requesterManifest, + }, + }; + try { + for (const [directory, manifest] of [ + [aliasPath, aliasManifest], + [requesterPath, requesterManifest], + ] as const) { + fs.mkdirSync(path.join(root, directory), { recursive: true }); + fs.writeFileSync( + path.join(root, directory, "package.json"), + `${JSON.stringify(manifest)}\n`, + ); + } + fs.writeFileSync(path.join(root, "package-lock.json"), `${JSON.stringify(lock)}\n`); + + normalizeOpenClawSignatureAlias(root); + + const normalizedLock = JSON.parse( + fs.readFileSync(path.join(root, "package-lock.json"), "utf-8"), + ); + const normalizedRequester = createRequire(import.meta.url)( + path.join(root, requesterPath, "package.json"), + ); + expect(fs.existsSync(path.join(root, aliasPath))).toBe(false); + expect(fs.existsSync(path.join(root, actualPath, "package.json"))).toBe(true); + expect(normalizedLock.packages[aliasPath]).toBeUndefined(); + expect(normalizedLock.packages[actualPath]).toMatchObject(aliasManifest); + expect(normalizedLock.packages[requesterPath].dependencies).toEqual({ + "@nolyfill/domexception": "1.0.28", + }); + expect(normalizedRequester.dependencies).toEqual({ + "@nolyfill/domexception": "1.0.28", + }); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } + }); +}); diff --git a/test/sandbox-build-context.test.ts b/test/sandbox-build-context.test.ts index 9fa7c6e4c06..f98d95bfb1c 100644 --- a/test/sandbox-build-context.test.ts +++ b/test/sandbox-build-context.test.ts @@ -39,7 +39,7 @@ describe("sandbox build context staging", () => { path.join("ci", "npm-audit-exceptions.json"), `${JSON.stringify({ schemaVersion: 1, exceptions: [] })}\n`, ); - for (const runtimeName of ["mcporter-runtime", "wechat-runtime"]) { + for (const runtimeName of ["mcporter-runtime", "openclaw-runtime", "wechat-runtime"]) { for (const fileName of ["package.json", "package-lock.json"]) { writeFixture( path.join("agents", "openclaw", runtimeName, fileName), @@ -189,7 +189,7 @@ describe("sandbox build context staging", () => { } function expectStagedOpenClawRuntimeGraphs(buildCtx: string, sourceRoot: string) { - for (const runtimeName of ["mcporter-runtime", "wechat-runtime"]) { + for (const runtimeName of ["mcporter-runtime", "openclaw-runtime", "wechat-runtime"]) { const runtimeDir = path.join(buildCtx, "agents", "openclaw", runtimeName); expect(fs.readdirSync(runtimeDir).sort()).toEqual(["package-lock.json", "package.json"]); for (const fileName of ["package.json", "package-lock.json"]) {