diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..bbd8bb2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,24 @@ +version: 2 + +# Third-party actions are pinned to immutable commit SHAs. SHAs never move on +# their own, so this entry is what keeps them current — without it the pins rot. +# +# Only the github-actions ecosystem is enabled. npm version-update PRs are +# deliberately off: `npm ci` installs strictly from the lockfile and `.npmrc` +# blocks dependency install scripts, so drift is already contained, and a PR +# per release is noise nobody reads. Dependabot *alerts* are enabled in repo +# settings and are the signal layer we do want; automated *security-update* PRs +# are off for the same reason — advisories get triaged against real exposure, +# not auto-patched. +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + # One PR for all action bumps instead of one per action. + groups: + actions: + patterns: + - "*" + commit-message: + prefix: "ci" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30425c5..9cfc00c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,19 @@ jobs: node-version: ${{ matrix.node-version }} cache: npm - run: npm ci - - run: npx tsc --noEmit - - run: npx biome check . - - run: npx vitest --run + # Blocking. Every installed dependency must carry a valid npm registry + # signature, so a tampered or unsigned tarball fails the build. + - run: npm audit signatures + # Blocking. This tree has no known advisories in its production + # dependencies today, so keep it that way: fix, replace, or drop the + # dependency rather than loosening the gate. + - run: npm audit --omit=dev + # `--no-install` keeps these on the binaries `npm ci` just installed and + # verified. Without it, npx silently fetches an unpinned package from the + # registry whenever a tool is missing from the lockfile, which lands + # unreviewed code in the job right after the gates above cleared it. + - run: npx --no-install tsc --noEmit + - run: npx --no-install vitest --run + # No lint step: the Biome CLI is not a declared dependency here, so + # invoking it would download an unreviewed binary on every run. Wiring up + # @biomejs/biome, and fixing what it reports, is a separate change. diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml index b624cbf..c5f442d 100644 --- a/.github/workflows/claude-pr-review.yml +++ b/.github/workflows/claude-pr-review.yml @@ -19,12 +19,23 @@ jobs: - uses: actions/checkout@v4 - name: Install OpenProse skill + # Pinned to a commit rather than tracking the default branch. This skill + # becomes the instructions Claude follows in a job that holds an API key + # and can write to pull requests, so a commit pushed to that repository + # would otherwise change behaviour here with nothing reviewed on this + # side. Bump deliberately. + env: + PROSE_REF: f7fa6770c4bf46d8af23215734ac5f16e5c3ee96 run: | - git clone --depth 1 https://github.com/openprose/prose.git /tmp/prose + set -euo pipefail + git init --quiet /tmp/prose + git -C /tmp/prose fetch --depth 1 --quiet \ + https://github.com/openprose/prose.git "${PROSE_REF}" + git -C /tmp/prose checkout --quiet FETCH_HEAD mkdir -p .claude/skills cp -r /tmp/prose/skills/open-prose .claude/skills/open-prose - - uses: anthropics/claude-code-action@v1 + - uses: anthropics/claude-code-action@c038e4dcdedfbbca18dfb17df35a17e40ded4ddc # v1.0.186 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} prompt: "prose run pr-review.prose" diff --git a/.github/workflows/press-eval-full.yml b/.github/workflows/press-eval-full.yml index 3b2ddb8..85190ed 100644 --- a/.github/workflows/press-eval-full.yml +++ b/.github/workflows/press-eval-full.yml @@ -4,14 +4,16 @@ on: workflow_dispatch: inputs: tier: - description: "Eval tier: quick (3 cheap), standard (6 default), full (all)" + description: "Eval tier" required: false default: "standard" + type: choice + options: [quick, standard, full] model: - description: "Override model for all evals" + description: "Override model for all evals (provider/name form)" required: false concurrency: - description: "Max parallel evals" + description: "Max parallel evals (1-10)" required: false default: "3" @@ -26,6 +28,12 @@ jobs: - uses: actions/checkout@v4 with: repository: openprose/prose + # Pinned: without a ref this tracks that repository's default branch, + # so a commit made there would change what runs in this job — which + # holds a provider key — with no change reviewed here. Bump + # deliberately. + ref: f7fa6770c4bf46d8af23215734ac5f16e5c3ee96 + persist-credentials: false path: prose - uses: actions/setup-node@v4 @@ -36,13 +44,39 @@ jobs: - run: npm ci - name: Run Press evals + # Dispatch inputs travel through the environment and are validated before + # use. Interpolating them straight into this script would let a dispatch + # value containing shell syntax execute as code, in a job that holds a + # provider key. `--no-install` keeps tsx on the lockfile's copy. run: | - npx tsx src/eval-pipeline.ts \ - --tier ${{ inputs.tier || 'standard' }} \ - --concurrency ${{ inputs.concurrency || '3' }} \ - ${{ inputs.model && format('--model {0}', inputs.model) || '' }} + set -euo pipefail + + case "${TIER}" in + quick|standard|full) ;; + *) echo "::error::Invalid tier: ${TIER}" >&2; exit 1 ;; + esac + + if ! printf '%s' "${CONCURRENCY}" | grep -qE '^([1-9]|10)$'; then + echo "::error::concurrency must be an integer from 1 to 10" >&2 + exit 1 + fi + + args=(--tier "${TIER}" --concurrency "${CONCURRENCY}") + + if [ -n "${MODEL}" ]; then + if ! printf '%s' "${MODEL}" | grep -qE '^[A-Za-z0-9._-]+/[A-Za-z0-9._:-]+$'; then + echo "::error::model must look like provider/name" >&2 + exit 1 + fi + args+=(--model "${MODEL}") + fi + + npx --no-install tsx src/eval-pipeline.ts "${args[@]}" env: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + TIER: ${{ inputs.tier || 'standard' }} + CONCURRENCY: ${{ inputs.concurrency || '3' }} + MODEL: ${{ inputs.model }} timeout-minutes: 25 - name: Upload eval results diff --git a/.github/workflows/press-eval.yml b/.github/workflows/press-eval.yml index 0dd144a..c1f9eb5 100644 --- a/.github/workflows/press-eval.yml +++ b/.github/workflows/press-eval.yml @@ -27,7 +27,9 @@ jobs: - run: npm ci - name: Run Press evals (quick tier) - run: npx tsx src/eval-pipeline.ts --tier quick --concurrency 3 + # `--no-install` so this runs the lockfile's tsx rather than fetching an + # unpinned one from the registry into a job holding a provider key. + run: npx --no-install tsx src/eval-pipeline.ts --tier quick --concurrency 3 env: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} timeout-minutes: 8 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79febe5..cc15d16 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,21 +5,30 @@ on: branches: [main] permissions: - contents: write + contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + # Nothing here writes to the repository, so leave no git credential on + # disk for the build and test steps to reach. + persist-credentials: false - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - - run: npx tsc --noEmit - - run: npx biome check . - - run: npx vitest --run + # Blocking, and repeated from pull-request CI on purpose: this workflow + # runs on push, so it is the only check a change merged without a pull + # request ever sees. + - run: npm audit signatures + - run: npm audit --omit=dev + # See ci.yml for why these carry `--no-install` and why there is no lint step. + - run: npx --no-install tsc --noEmit + - run: npx --no-install vitest --run - name: Get version id: version @@ -33,8 +42,21 @@ jobs: # NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # # To enable: add NPM_TOKEN to repository secrets, # # and add `registry-url: https://registry.npmjs.org` to setup-node above. + # # + # # Heads up: .npmrc sets `ignore-scripts=true` so no dependency install + # # script ever runs. That setting is not limited to dependencies — it also + # # suppresses this package's own `prepublishOnly` hook + # # (`npm run clean && npm run build`), so `npm publish` will NOT build + # # dist/ for you. The explicit `npm run build` step above is what produces + # # the artifact; keep it before any publish step (or run + # # `npm run clean && npm run build` here) so a stale or empty dist/ is + # # never published. # - name: Create GitHub Release # run: gh release create "v${{ steps.version.outputs.version }}" --generate-notes # env: # GH_TOKEN: ${{ github.token }} + # # To enable: this needs `contents: write`, which the workflow no longer + # # grants. Give it to a separate release job rather than widening the + # # build job — the build runs the whole dev dependency graph, and the + # # release step needs nothing from it but the finished dist/. diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..9f27ac1 --- /dev/null +++ b/.npmrc @@ -0,0 +1,8 @@ +# Supply-chain hardening: never run a dependency's install lifecycle scripts. +# npm has no allow-list model (pnpm's onlyBuiltDependencies), so this is the +# blunt equivalent — it applies to CI (`npm ci`) and developer laptops alike. +# +# Caveat: this also suppresses THIS project's own lifecycle scripts, including +# `prepublishOnly` (clean + build). Any publish path must build explicitly +# first — see the note in .github/workflows/release.yml. +ignore-scripts=true diff --git a/package-lock.json b/package-lock.json index a9f993b..0bb4ee8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ }, "devDependencies": { "@types/node": "^24.3.0", + "tsx": "^4.23.12", "typescript": "^5.7.3", "vitest": "^3.2.4" }, @@ -1427,6 +1428,509 @@ "node": ">=14.0.0" } }, + "node_modules/tsx": { + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", + "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", diff --git a/package.json b/package.json index 4574e81..2aa4c2f 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ }, "devDependencies": { "@types/node": "^24.3.0", + "tsx": "^4.23.12", "typescript": "^5.7.3", "vitest": "^3.2.4" }